diff --git a/.gitignore b/.gitignore index 4eef6be..e35bf4a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ docs .workspace.codex .env -.vscode \ No newline at end of file +.vscode +.codex \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0d47eba --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/kvstore"] + path = third_party/kvstore + url = http://gitlab.0voice.com/lianyiheng/9.1-kvstore.git diff --git a/README.md b/README.md index 5c4b727..cdc78a6 100644 --- a/README.md +++ b/README.md @@ -8,23 +8,8 @@ protoc \ ``` ```shell -# ai-chat-web +git submodule update --init --recursive -# ai-chat-backend -docker build -t ai-chat-backend:1.0.0 . +./script/run.sh -# ai-chat-service -docker build -t ai-chat-service:1.0.0 . - -# chatgpt-web-frontend -HUSKY=0 pnpm bootstrap -pnpm dev - -docker build -t chatgpt-web-frontend:1.0.0 . - -# tokenizer -docker build -t tokenizer:1.0.0 . - -# keywords-filter -docker build -t keywords-filter:1.0.0 . ``` \ No newline at end of file diff --git a/ai-chat-service/docker.config.yaml b/ai-chat-service/docker.config.yaml index e819b02..a9c6240 100644 --- a/ai-chat-service/docker.config.yaml +++ b/ai-chat-service/docker.config.yaml @@ -17,9 +17,9 @@ chat: bot_desc: "你是一个AI助手,我需要你模拟一名资深的软件工程师来回答我的问题" min_response_tokens: 600 redis: - host: "redis" - port: 6379 - pwd: "" + host: "host.docker.internal" + port: 8888 + pwd: "123456" dependOn: sensitive: address: "sensitive-filter:50053" diff --git a/ai-chat-stack/compose.yaml b/ai-chat-stack/compose.yaml index 8205301..26167f8 100644 --- a/ai-chat-stack/compose.yaml +++ b/ai-chat-stack/compose.yaml @@ -74,6 +74,8 @@ services: - .env volumes: - ./configs/ai-chat-service.yaml:/app/config.yaml:ro + extra_hosts: + - "host.docker.internal:host-gateway" ports: - "50055:50055" depends_on: diff --git a/ai-chat-stack/configs/ai-chat-service.yaml b/ai-chat-stack/configs/ai-chat-service.yaml index 09919f7..7710524 100644 --- a/ai-chat-stack/configs/ai-chat-service.yaml +++ b/ai-chat-stack/configs/ai-chat-service.yaml @@ -17,9 +17,9 @@ chat: bot_desc: "你是一个AI助手,我需要你模拟一名资深的软件工程师来回答我的问题" min_response_tokens: 600 redis: - host: "127.0.0.1" + host: "host.docker.internal" port: 8888 - pwd: "" + pwd: "123456" dependOn: sensitive: address: "sensitive-filter:50053" diff --git a/ai-chat-web/src/locales/en-US.ts b/ai-chat-web/src/locales/en-US.ts index cb791ab..ff8bc90 100644 --- a/ai-chat-web/src/locales/en-US.ts +++ b/ai-chat-web/src/locales/en-US.ts @@ -50,6 +50,8 @@ export default { showRawText: 'Show as raw text', sourceSemantic: 'Semantic Match', sourceLlm: 'LLM Output', + inputTokensShort: 'Input', + outputTokensShort: 'Output', inputTokens: 'Input {count}', outputTokens: 'Output {count}', sessionTokens: 'Session {count} tokens', diff --git a/ai-chat-web/src/locales/zh-CN.ts b/ai-chat-web/src/locales/zh-CN.ts index 16d05dd..364f9e7 100644 --- a/ai-chat-web/src/locales/zh-CN.ts +++ b/ai-chat-web/src/locales/zh-CN.ts @@ -50,6 +50,8 @@ export default { showRawText: '显示原文', sourceSemantic: '语义匹配', sourceLlm: '大模型输出', + inputTokensShort: '输入', + outputTokensShort: '输出', inputTokens: 'Input {count}', outputTokens: 'Output {count}', sessionTokens: '本轮消耗 {count} tokens', diff --git a/ai-chat-web/src/locales/zh-TW.ts b/ai-chat-web/src/locales/zh-TW.ts index 111f37b..aa62786 100644 --- a/ai-chat-web/src/locales/zh-TW.ts +++ b/ai-chat-web/src/locales/zh-TW.ts @@ -50,6 +50,8 @@ export default { showRawText: '顯示原文', sourceSemantic: '語義匹配', sourceLlm: '大模型輸出', + inputTokensShort: '輸入', + outputTokensShort: '輸出', inputTokens: 'Input {count}', outputTokens: 'Output {count}', sessionTokens: '本輪消耗 {count} tokens', diff --git a/ai-chat-web/src/views/chat/components/Message/index.vue b/ai-chat-web/src/views/chat/components/Message/index.vue index 332c9b2..8c40a41 100644 --- a/ai-chat-web/src/views/chat/components/Message/index.vue +++ b/ai-chat-web/src/views/chat/components/Message/index.vue @@ -54,28 +54,43 @@ const sourceLabel = computed(() => { const sourceClass = computed(() => { switch (props.messageMeta?.source) { case 'semantic_match': - return 'border-sky-200 bg-sky-50 text-sky-700 dark:border-sky-900/60 dark:bg-sky-950/40 dark:text-sky-300' case 'llm': - return 'border-amber-200 bg-amber-50 text-amber-700 dark:border-amber-900/60 dark:bg-amber-950/40 dark:text-amber-300' + return 'border-[#d8e2eb] bg-[#f7fafc] text-[#52606d] dark:border-neutral-700 dark:bg-[#1a1d22] dark:text-neutral-200' default: return 'border-[#d0d7de] bg-white text-[#57606a] dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-300' } }) -const inputUsageLabel = computed(() => { - const usage = props.messageMeta?.usage - if (!usage || props.inversion) - return '' - - return t('chat.inputTokens', { count: usage.prompt_tokens }) +const sourceIconClass = computed(() => { + switch (props.messageMeta?.source) { + case 'semantic_match': + return 'bg-emerald-50 text-emerald-600 shadow-[inset_0_0_0_1px_rgba(52,211,153,0.18)] dark:bg-emerald-950/35 dark:text-emerald-300' + case 'llm': + return 'bg-slate-100 text-slate-500 shadow-[inset_0_0_0_1px_rgba(148,163,184,0.18)] dark:bg-slate-800/70 dark:text-slate-300' + default: + return 'bg-white/90 text-[#52606d] shadow-[inset_0_0_0_1px_rgba(130,140,150,0.14)] dark:bg-neutral-800 dark:text-neutral-300' + } }) -const outputUsageLabel = computed(() => { +const tokenStats = computed(() => { const usage = props.messageMeta?.usage if (!usage || props.inversion) - return '' + return [] - return t('chat.outputTokens', { count: usage.completion_tokens }) + return [ + { + key: 'input', + label: t('chat.inputTokensShort'), + count: usage.prompt_tokens, + icon: 'ri:arrow-right-up-line', + }, + { + key: 'output', + label: t('chat.outputTokensShort'), + count: usage.completion_tokens, + icon: 'ri:sparkling-line', + }, + ] }) const options = computed(() => { @@ -138,30 +153,29 @@ function handleRegenerate() {
{{ dateTime }}
- + + + {{ sourceLabel }}
- - {{ inputUsageLabel }} -
-
- - {{ outputUsageLabel }} + + + + {{ item.label }} + {{ item.count }}
diff --git a/scripts/clean_faiss_and_kv.sh b/scripts/clean_faiss_and_kv.sh new file mode 100755 index 0000000..a8657ea --- /dev/null +++ b/scripts/clean_faiss_and_kv.sh @@ -0,0 +1,2 @@ +rm -rf ../faiss/indexes/global/global_qa.index +rm -rf ../kvstore/data/kvs_oplog.db \ No newline at end of file diff --git a/scripts/compiler.sh b/scripts/compiler.sh new file mode 100755 index 0000000..fa73b89 --- /dev/null +++ b/scripts/compiler.sh @@ -0,0 +1 @@ +cd third_party/kvstore/ && make \ No newline at end of file diff --git a/scripts/inspect_ai_redis.sh b/scripts/inspect_ai_redis.sh deleted file mode 100755 index 42f4ae8..0000000 --- a/scripts/inspect_ai_redis.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -REDIS_HOST="${REDIS_HOST:-127.0.0.1}" -REDIS_PORT="${REDIS_PORT:-8888}" -REDIS_DB="${REDIS_DB:-0}" -REDIS_PASSWORD="${REDIS_PASSWORD:-}" -PATTERNS=("ai_chat_service_*") - -usage() { - cat <<'EOF' -Usage: - inspect_ai_redis.sh [options] - -Options: - --host Redis host, default: 127.0.0.1 - --port Redis port, default: 8888 - --db Redis db index, default: 0 - --password Redis password - --pattern Key pattern, can be repeated. Default: ai_chat_service_* - --help Show this help - -Environment: - REDIS_HOST - REDIS_PORT - REDIS_DB - REDIS_PASSWORD - -Examples: - ./scripts/inspect_ai_redis.sh - ./scripts/inspect_ai_redis.sh --pattern 'ai_chat_service_*' --pattern 'foo_*' - REDIS_PORT=6379 ./scripts/inspect_ai_redis.sh --host 10.0.0.8 -EOF -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --host) - REDIS_HOST="$2" - shift 2 - ;; - --port) - REDIS_PORT="$2" - shift 2 - ;; - --db) - REDIS_DB="$2" - shift 2 - ;; - --password) - REDIS_PASSWORD="$2" - shift 2 - ;; - --pattern) - if [[ "${PATTERNS[*]}" == "ai_chat_service_*" && "${#PATTERNS[@]}" -eq 1 ]]; then - PATTERNS=() - fi - PATTERNS+=("$2") - shift 2 - ;; - --help|-h) - usage - exit 0 - ;; - *) - echo "Unknown argument: $1" >&2 - usage >&2 - exit 1 - ;; - esac -done - -if ! command -v redis-cli >/dev/null 2>&1; then - echo "redis-cli not found in PATH" >&2 - exit 1 -fi - -redis_cmd() { - if [[ -n "$REDIS_PASSWORD" ]]; then - redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -n "$REDIS_DB" -a "$REDIS_PASSWORD" --raw "$@" - else - redis-cli -h "$REDIS_HOST" -p "$REDIS_PORT" -n "$REDIS_DB" --raw "$@" - fi -} - -print_string() { - local key="$1" - redis_cmd GET "$key" -} - -print_hash() { - local key="$1" - redis_cmd HGETALL "$key" | awk 'NR % 2 == 1 { printf "%s: ", $0; next } { print $0 }' -} - -print_list() { - local key="$1" - redis_cmd LRANGE "$key" 0 -1 -} - -print_set() { - local key="$1" - redis_cmd SMEMBERS "$key" -} - -print_zset() { - local key="$1" - redis_cmd ZRANGE "$key" 0 -1 WITHSCORES | awk 'NR % 2 == 1 { printf "%s => ", $0; next } { print $0 }' -} - -print_stream() { - local key="$1" - redis_cmd XRANGE "$key" - + -} - -if ! redis_cmd PING >/dev/null 2>&1; then - echo "Failed to connect to Redis at ${REDIS_HOST}:${REDIS_PORT}, db=${REDIS_DB}" >&2 - exit 1 -fi - -declare -A seen_keys=() -keys=() - -for pattern in "${PATTERNS[@]}"; do - while IFS= read -r key; do - [[ -z "$key" ]] && continue - if [[ -z "${seen_keys[$key]:-}" ]]; then - seen_keys["$key"]=1 - keys+=("$key") - fi - done < <(redis_cmd --scan --pattern "$pattern") -done - -if [[ "${#keys[@]}" -eq 0 ]]; then - echo "No keys matched patterns: ${PATTERNS[*]}" - exit 0 -fi - -printf 'Redis: %s:%s db=%s\n' "$REDIS_HOST" "$REDIS_PORT" "$REDIS_DB" -printf 'Patterns: %s\n' "${PATTERNS[*]}" -printf 'Matched keys: %s\n\n' "${#keys[@]}" - -for key in "${keys[@]}"; do - key_type="$(redis_cmd TYPE "$key" | tr -d '\r')" - ttl="$(redis_cmd TTL "$key" | tr -d '\r')" - - echo "KEY: $key" - echo "TYPE: $key_type" - echo "TTL: $ttl" - echo "VALUE:" - - case "$key_type" in - string) - print_string "$key" - ;; - hash) - print_hash "$key" - ;; - list) - print_list "$key" - ;; - set) - print_set "$key" - ;; - zset) - print_zset "$key" - ;; - stream) - print_stream "$key" - ;; - *) - echo "(unsupported type: $key_type)" - ;; - esac - - echo -done diff --git a/scripts/run_kv.sh b/scripts/run_kv.sh new file mode 100755 index 0000000..b5637f6 --- /dev/null +++ b/scripts/run_kv.sh @@ -0,0 +1,4 @@ +cd third_party/kvstore/ +stdbuf -oL -eL nohup ./kvstore > /tmp/kvlog 2>&1 & +echo $! > /tmp/kvstore.pid +cat /tmp/kvstore.pid \ No newline at end of file diff --git a/third_party/kvstore b/third_party/kvstore new file mode 160000 index 0000000..2f59d3c --- /dev/null +++ b/third_party/kvstore @@ -0,0 +1 @@ +Subproject commit 2f59d3ce27841204e09f5ca4a365a4805656683b