Usability pass: tool registry, TUI polish, saved keys and settings

Tools
- Table-driven tool registry with permission classes and default/minimal
  profiles; new tools: edit_file, list_dir, glob, grep, delete_file,
  move_file, todo_write, web_fetch, ask_user (plus read_file paging).
- Built-in Myers diff; write tools return unified diffs.
- Persistent permission rules (.agent/permissions.json), per-turn undo
  snapshots (.agent/undo), AGENTS.md project notes, auto-compact at 80%
  of the model context, cancellable run_shell with process-group kill.

TUI
- Table-driven slash commands: /provider /key /effort /status /tools
  /permissions /yolo /undo /retry /diff /export /copy /rename /delete
  /init /max-steps /todo, with / and @ completion.
- Permission modal with diff and prefix rules, ask_user modal, todo
  panel, header with model/effort/context, bracketed paste, line editing,
  wide-char cursor, NO_COLOR and ASCII fallback; Esc never quits.

Startup and configuration
- API keys are entered in the TUI (hidden) and saved per provider in
  <config>/credentials (0600); env variables are only a fallback.
- Last provider, per-provider model and reasoning effort are remembered
  in <config>/settings.json; --effort/-e and /effort send
  reasoning_effort (low|medium|high|max) to models that accept it.
- DeepSeek preset uses deepseek-flash / deepseek-v4-pro.
- Session flags --new/--continue/--resume/--list-sessions, --yolo,
  --confirm.

Compatibility and tests
- ncurses detection, macOS/Homebrew support, make install, XDG config
  dir, POSIX cleanups; Gitea CI (gcc/clang/asan/musl/macOS).
- Unit tests for diff, fs tools, registry, undo/perms and config;
  e2e fixtures multi_file_refactor and ask_or_todo; vendored cJSON.
This commit is contained in:
WHC
2026-10-05 01:47:01 +08:00
parent 9877ff48b6
commit 2fde898da0
105 changed files with 19348 additions and 3786 deletions
+75
View File
@@ -0,0 +1,75 @@
name: ci
on:
push:
pull_request:
jobs:
linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
cc: [gcc, clang]
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
SUDO=$(command -v sudo || true)
$SUDO apt-get update
$SUDO apt-get install -y --no-install-recommends \
build-essential clang cmake pkg-config python3 \
libcurl4-openssl-dev libncurses-dev
- name: Build (warnings are errors)
run: make CC=${{ matrix.cc }} CFLAGS="-std=c11 -Wall -Wextra -O2 -g -Werror" -j"$(nproc)"
- name: Unit tests
run: make CC=${{ matrix.cc }} check
- name: CMake build + ctest
run: |
cmake -B build -DCMAKE_C_COMPILER=${{ matrix.cc }} -DCST_BUILD_TUI=ON
cmake --build build -j"$(nproc)"
ctest --test-dir build --output-on-failure
- name: Smoke test
run: |
./cst-agent --help
./cst-agent --workspace "$PWD" --list-sessions
./cst-agent --demo-ctl --workspace .
sanitizers:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: |
SUDO=$(command -v sudo || true)
$SUDO apt-get update
$SUDO apt-get install -y --no-install-recommends \
build-essential pkg-config libcurl4-openssl-dev libncurses-dev
- name: ASan + UBSan
run: make asan
musl:
runs-on: ubuntu-latest
container: alpine:3.20
steps:
- name: Install dependencies
run: apk add --no-cache build-base pkgconf curl-dev ncurses-dev git nodejs bash python3
- uses: actions/checkout@v4
- name: Build + unit tests
run: |
make CFLAGS="-std=c11 -Wall -Wextra -O2 -Werror" -j"$(nproc)"
make check
# Gitea instances rarely have macOS runners; set the repository variable
# HAVE_MACOS_RUNNER=true once one is registered.
macos:
if: ${{ vars.HAVE_MACOS_RUNNER == 'true' }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: brew install ncurses curl pkg-config
- name: Build + unit tests
run: |
make -j"$(sysctl -n hw.ncpu)"
make check
+7 -2
View File
@@ -5,10 +5,15 @@ tests/test_*
!tests/test_*.c !tests/test_*.c
session.jsonl session.jsonl
.agent/ .agent/
fixtures/mini_calc/out/ out/
fixtures/*/out/
fixtures/mini_calc/calc fixtures/mini_calc/calc
fixtures/snake_cpp/snake fixtures/snake_cpp/snake
fixtures/snake_cpp/out/ fixtures/makefile_tab/hello
fixtures/makefile_tab/tests/actual.txt
fixtures/test_fix_test/test_strutil
fixtures/test_fix_test/.test_runs
fixtures/big_file/lines.txt
build/ build/
build-*/ build-*/
dist/ dist/
+52 -11
View File
@@ -42,12 +42,25 @@ add_custom_target(sashidown DEPENDS "${SASHIDOWN_A}")
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
set(CST_SOURCES set(CJSON_DIR "${CMAKE_CURRENT_SOURCE_DIR}/vendor/cjson")
set(CST_CORE_SOURCES
vendor/cjson/cJSON.c
src/util.c
src/ctl_parse.c src/ctl_parse.c
src/os_compat.c src/os_compat.c
src/sandbox.c src/sandbox.c
src/session.c src/session.c
src/tools/tools.c src/tools/tools.c
src/tools/patch.c
src/tools/registry.c
src/tools/fs_tools.c
src/tools/agent_tools.c
src/tools/web.c
src/tools/diff.c
src/undo.c
src/permissions.c
src/settings.c
src/llm_openai.c src/llm_openai.c
src/agent_loop.c src/agent_loop.c
src/agent_store.c src/agent_store.c
@@ -55,8 +68,9 @@ set(CST_SOURCES
src/creds.c src/creds.c
src/mm_tool.c src/mm_tool.c
src/models.c src/models.c
src/main.c src/metrics.c
) )
set(CST_SOURCES ${CST_CORE_SOURCES} src/main.c)
if(CST_BUILD_TUI) if(CST_BUILD_TUI)
list(APPEND CST_SOURCES src/tui.c) list(APPEND CST_SOURCES src/tui.c)
@@ -70,6 +84,7 @@ add_dependencies(cst-agent sashidown)
target_include_directories(cst-agent PRIVATE target_include_directories(cst-agent PRIVATE
include include
"${SD_DIR}/include" "${SD_DIR}/include"
"${CJSON_DIR}"
) )
target_compile_definitions(cst-agent PRIVATE _GNU_SOURCE) target_compile_definitions(cst-agent PRIVATE _GNU_SOURCE)
@@ -128,19 +143,40 @@ endif()
# --- tests --- # --- tests ---
enable_testing() enable_testing()
foreach(t test_ctl_frontier test_json_baseline test_replay_starvation test_mm_tool) set(_CST_SMALL_TESTS test_ctl_frontier test_ctl_heredoc test_json_baseline
if(t STREQUAL test_ctl_frontier) test_replay_starvation test_replay_real test_mm_tool)
add_executable(${t} tests/test_ctl_frontier.c src/ctl_parse.c) foreach(t ${_CST_SMALL_TESTS})
if(t STREQUAL test_ctl_frontier OR t STREQUAL test_ctl_heredoc)
add_executable(${t} tests/${t}.c src/ctl_parse.c)
elseif(t STREQUAL test_json_baseline) elseif(t STREQUAL test_json_baseline)
add_executable(${t} tests/test_json_baseline.c src/json_baseline.c) add_executable(${t} tests/${t}.c src/json_baseline.c vendor/cjson/cJSON.c)
elseif(t STREQUAL test_replay_starvation) elseif(t STREQUAL test_replay_starvation OR t STREQUAL test_replay_real)
add_executable(${t} tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c) add_executable(${t} tests/${t}.c src/ctl_parse.c src/json_baseline.c vendor/cjson/cJSON.c)
else() else()
add_executable(${t} tests/test_mm_tool.c src/mm_tool.c) add_executable(${t} tests/test_mm_tool.c src/mm_tool.c)
endif() endif()
target_include_directories(${t} PRIVATE include) target_include_directories(${t} PRIVATE include "${CJSON_DIR}")
target_compile_definitions(${t} PRIVATE _GNU_SOURCE) target_compile_definitions(${t} PRIVATE _GNU_SOURCE)
add_test(NAME ${t} COMMAND ${t}) if(NOT WIN32)
target_link_libraries(${t} PRIVATE m)
endif()
add_test(NAME ${t} COMMAND ${t} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
endforeach()
# Tests that link the whole core (no TUI).
foreach(t test_llm_body test_sse_decode test_apply_patch test_session_roundtrip
test_shell test_diff test_tools_fs test_registry
test_undo test_config)
add_executable(${t} tests/${t}.c ${CST_CORE_SOURCES})
target_include_directories(${t} PRIVATE include "${CJSON_DIR}")
target_compile_definitions(${t} PRIVATE _GNU_SOURCE)
target_link_libraries(${t} PRIVATE CURL::libcurl Threads::Threads)
if(NOT WIN32)
target_link_libraries(${t} PRIVATE m)
else()
target_link_libraries(${t} PRIVATE ws2_32 crypt32 secur32 bcrypt advapi32)
endif()
add_test(NAME ${t} COMMAND ${t} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}")
endforeach() endforeach()
# --- busybox bundle helpers --- # --- busybox bundle helpers ---
@@ -154,7 +190,12 @@ if(CST_BUNDLE_BUSYBOX)
endif() endif()
endif() endif()
install(TARGETS cst-agent RUNTIME DESTINATION .) if(WIN32)
install(TARGETS cst-agent RUNTIME DESTINATION .)
else()
include(GNUInstallDirs)
install(TARGETS cst-agent RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
endif()
message(STATUS "Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER}") message(STATUS "Compiler: ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER}")
message(STATUS "CST_BUILD_TUI=${CST_BUILD_TUI}") message(STATUS "CST_BUILD_TUI=${CST_BUILD_TUI}")
+108 -23
View File
@@ -1,16 +1,65 @@
CC ?= gcc CC ?= gcc
CFLAGS ?= -std=c11 -Wall -Wextra -O2 -g CFLAGS ?= -std=c11 -Wall -Wextra -O2 -g
SD_DIR := vendor/sashidown SD_DIR := vendor/sashidown
CPPFLAGS += -Iinclude -I$(SD_DIR)/include -D_XOPEN_SOURCE_EXTENDED CJSON_DIR := vendor/cjson
LDFLAGS += -L$(SD_DIR) PKG_CONFIG ?= pkg-config
LDLIBS += -lsashidown -lm -lcurl -lncursesw -lpthread PREFIX ?= /usr/local
DESTDIR ?=
UNAME_S := $(shell uname -s 2>/dev/null)
SRC = \ # macOS: Homebrew ncurses/curl are keg-only, so point pkg-config at them.
# (macOS ships GNU make 3.81, whose $(shell) ignores exported variables.)
PKG_ENV :=
ifeq ($(UNAME_S),Darwin)
BREW_PREFIX := $(shell brew --prefix 2>/dev/null)
ifneq ($(BREW_PREFIX),)
PKG_ENV := PKG_CONFIG_PATH="$(BREW_PREFIX)/opt/ncurses/lib/pkgconfig:$(BREW_PREFIX)/opt/curl/lib/pkgconfig:$$PKG_CONFIG_PATH"
endif
CPPFLAGS += -D_DARWIN_C_SOURCE
endif
PKGC = $(PKG_ENV) $(PKG_CONFIG)
# Wide-character curses: ncursesw, else ncurses (macOS/BSD build it wide).
CURSES_PC := $(shell for p in ncursesw ncurses; do $(PKGC) --exists $$p 2>/dev/null && { echo $$p; exit 0; }; done)
ifneq ($(CURSES_PC),)
CURSES_CFLAGS := $(shell $(PKGC) --cflags $(CURSES_PC))
CURSES_LIBS := $(shell $(PKGC) --libs $(CURSES_PC))
else ifeq ($(UNAME_S),Darwin)
CURSES_LIBS := -lncurses
else
CURSES_LIBS := -lncursesw
endif
CURL_CFLAGS := $(shell $(PKGC) --cflags libcurl 2>/dev/null)
CURL_LIBS := $(shell $(PKGC) --libs libcurl 2>/dev/null)
ifeq ($(strip $(CURL_LIBS)),)
CURL_LIBS := -lcurl
endif
PKG_CFLAGS := $(CURL_CFLAGS) $(CURSES_CFLAGS)
PKG_LIBS := $(CURL_LIBS) $(CURSES_LIBS)
CPPFLAGS += -Iinclude -I$(SD_DIR)/include -I$(CJSON_DIR) -D_XOPEN_SOURCE_EXTENDED $(PKG_CFLAGS)
LDFLAGS += -L$(SD_DIR)
LDLIBS += -lsashidown -lm $(PKG_LIBS) -lpthread
CORE_SRC = \
$(CJSON_DIR)/cJSON.c \
src/util.c \
src/ctl_parse.c \ src/ctl_parse.c \
src/os_compat.c \ src/os_compat.c \
src/sandbox.c \ src/sandbox.c \
src/session.c \ src/session.c \
src/tools/tools.c \ src/tools/tools.c \
src/tools/patch.c \
src/tools/registry.c \
src/tools/fs_tools.c \
src/tools/agent_tools.c \
src/tools/web.c \
src/tools/diff.c \
src/undo.c \
src/permissions.c \
src/settings.c \
src/llm_openai.c \ src/llm_openai.c \
src/agent_loop.c \ src/agent_loop.c \
src/agent_store.c \ src/agent_store.c \
@@ -18,16 +67,36 @@ SRC = \
src/creds.c \ src/creds.c \
src/mm_tool.c \ src/mm_tool.c \
src/models.c \ src/models.c \
src/tui.c \ src/metrics.c
src/main.c
SRC = $(CORE_SRC) src/tui.c src/main.c
OBJ = $(SRC:.c=.o) OBJ = $(SRC:.c=.o)
HDRS = $(wildcard include/*.h) $(wildcard src/tools/*.h) $(CJSON_DIR)/cJSON.h
.PHONY: all clean check demo sashidown-lib cmake-gcc cmake-clang TESTS = \
tests/test_ctl_frontier \
tests/test_ctl_heredoc \
tests/test_json_baseline \
tests/test_replay_starvation \
tests/test_replay_real \
tests/test_mm_tool \
tests/test_llm_body \
tests/test_sse_decode \
tests/test_apply_patch \
tests/test_session_roundtrip \
tests/test_shell \
tests/test_diff \
tests/test_tools_fs \
tests/test_registry \
tests/test_undo \
tests/test_config
TEST_LIBS = -lm $(CURL_LIBS) -lpthread
.PHONY: all clean check demo sashidown-lib cmake-gcc cmake-clang asan install uninstall
all: sashidown-lib cst-agent all: sashidown-lib cst-agent
# Optional CMake entrypoints (same sources)
cmake-gcc: cmake-gcc:
cmake -B build-gcc -DCMAKE_C_COMPILER=gcc -DCST_BUILD_TUI=ON cmake -B build-gcc -DCMAKE_C_COMPILER=gcc -DCST_BUILD_TUI=ON
cmake --build build-gcc cmake --build build-gcc
@@ -43,35 +112,51 @@ sashidown-lib:
cst-agent: $(OBJ) $(SD_DIR)/libsashidown.a cst-agent: $(OBJ) $(SD_DIR)/libsashidown.a
$(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LDLIBS) $(CC) $(CFLAGS) -o $@ $(OBJ) $(LDFLAGS) $(LDLIBS)
src/%.o: src/%.c include/ctl.h include/cst_agent.h %.o: %.c $(HDRS)
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
src/tools/%.o: src/tools/%.c include/ctl.h include/cst_agent.h src/tui.o: src/tui.c $(HDRS) $(SD_DIR)/include/sashidown.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
src/tui.o: src/tui.c include/cst_agent.h $(SD_DIR)/include/sashidown.h
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
tests/test_ctl_frontier: tests/test_ctl_frontier.c src/ctl_parse.c include/ctl.h tests/test_ctl_frontier: tests/test_ctl_frontier.c src/ctl_parse.c include/ctl.h
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_ctl_frontier.c src/ctl_parse.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_ctl_frontier.c src/ctl_parse.c
tests/test_json_baseline: tests/test_json_baseline.c src/json_baseline.c include/cst_agent.h tests/test_ctl_heredoc: tests/test_ctl_heredoc.c src/ctl_parse.c include/ctl.h
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_json_baseline.c src/json_baseline.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_ctl_heredoc.c src/ctl_parse.c
tests/test_replay_starvation: tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c tests/test_json_baseline: tests/test_json_baseline.c src/json_baseline.c $(CJSON_DIR)/cJSON.c $(HDRS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_json_baseline.c src/json_baseline.c $(CJSON_DIR)/cJSON.c -lm
tests/test_replay_starvation: tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_replay_starvation.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c -lm
tests/test_replay_real: tests/test_replay_real.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_replay_real.c src/ctl_parse.c src/json_baseline.c $(CJSON_DIR)/cJSON.c -lm
tests/test_mm_tool: tests/test_mm_tool.c src/mm_tool.c include/mm_tool.h tests/test_mm_tool: tests/test_mm_tool.c src/mm_tool.c include/mm_tool.h
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_mm_tool.c src/mm_tool.c $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ tests/test_mm_tool.c src/mm_tool.c
check: tests/test_ctl_frontier tests/test_json_baseline tests/test_replay_starvation tests/test_mm_tool tests/test_%: tests/test_%.c $(CORE_SRC) $(HDRS)
./tests/test_ctl_frontier $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $< $(CORE_SRC) $(TEST_LIBS)
./tests/test_json_baseline
./tests/test_replay_starvation check: $(TESTS)
./tests/test_mm_tool @set -e; for t in $(TESTS); do echo "== $$t"; ./$$t; done
# AddressSanitizer + UBSan build of the agent and all tests.
asan:
$(MAKE) clean
$(MAKE) sashidown-lib
$(MAKE) CFLAGS="-std=c11 -Wall -Wextra -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=undefined -fno-omit-frame-pointer" cst-agent check
demo: cst-agent demo: cst-agent
./cst-agent --demo-ctl --workspace . ./cst-agent --demo-ctl --workspace .
clean: clean:
rm -f $(OBJ) cst-agent tests/test_ctl_frontier tests/test_json_baseline tests/test_replay_starvation tests/test_mm_tool rm -f $(OBJ) cst-agent $(TESTS)
install: cst-agent
install -d "$(DESTDIR)$(PREFIX)/bin"
install -m 0755 cst-agent "$(DESTDIR)$(PREFIX)/bin/cst-agent"
uninstall:
rm -f "$(DESTDIR)$(PREFIX)/bin/cst-agent"
+182 -56
View File
@@ -7,8 +7,8 @@ See [docs/theory_tool_cst.md](docs/theory_tool_cst.md).
## 能不能用来工作? ## 能不能用来工作?
**可以**:有监督的本地小改动 / 探索本仓库(TUI + 工具 + 权限确认)。 **可以**:有监督的本地改动 / 探索仓库(TUI + 13 个内置工具 + 带 diff 的权限确认 + `/undo`)。
**不宜**:无人值守长任务或当 CI 主力(网关偶发空答、无完整 diff UI)。 **不宜**:无人值守长任务或当 CI 主力(网关偶发空答;headless 模式默认自动批准)。
### 5 分钟试用 ### 5 分钟试用
@@ -19,13 +19,16 @@ git clone https://git.whcself.xyz/WHC/CST-agent.git && ./CST-agent/scripts/boots
# 已有仓库目录内: # 已有仓库目录内:
./scripts/bootstrap_linux.sh . ./scripts/bootstrap_linux.sh .
# OpenAI 官方 # 装到 PATH(或 sudo make install)
export OPENAI_API_KEY='sk-...' make install PREFIX=$HOME/.local
export OPENAI_BASE_URL='https://api.openai.com/v1'
export OPENAI_MODEL='gpt-4.1-mini' # 在项目目录里直接启动:首次会让你选 provider 并输入 API key(遮罩输入,按 provider 保存)
./cst-agent --workspace . cd ~/my-project && cst-agent
``` ```
之后再启动会沿用上次的 provider、模型和推理强度(low / medium / high / max),不需要任何参数或环境变量。
界面里用 `/provider` 切换(没保存过 key 的会当场询问),`/model`、`/effort` 切换模型和推理强度,`/key` 更换 key。
完整环境变量说明:[docs/env_openai.md](docs/env_openai.md)。 完整环境变量说明:[docs/env_openai.md](docs/env_openai.md)。
| 平台 | 一键脚本 | | 平台 | 一键脚本 |
@@ -41,21 +44,41 @@ export OPENAI_MODEL='gpt-4.1-mini'
```bash ```bash
./scripts/e2e_mini_calc.sh --baseline-only # 无 API:证明初始测试 RED ./scripts/e2e_mini_calc.sh --baseline-only # 无 API:证明初始测试 RED
./scripts/e2e_mini_calc.sh # 有 key:agent 修复后 make test ./scripts/e2e_mini_calc.sh --provider deepseek --protocol ctl # 有 key
``` ```
口径:`pass` / `partial` / `fail` — 见 [docs/eval_tool_cst.md](docs/eval_tool_cst.md)。 更多夹具(每个都有 `TASK.md` + `check.sh`):`makefile_tab`(写含 TAB 的 Makefile)、
`big_file`(写 5000 字节文件)、`test_fix_test`(先跑测试→修→再跑测试)、
`multi_file_refactor`(跨文件改名 + 修 bug,适合 `edit_file` / `grep`)、
`ask_or_todo`(必须用 `todo_write` 规划并全部完成;无人回答 `ask_user`)。
后两个需要 `--tools default`(脚本默认 `minimal`,即论文用的 4 个工具)。
```bash
scripts/e2e_fixture.sh makefile_tab --provider deepseek --protocol native-inc
scripts/e2e_fixture.sh multi_file_refactor --provider deepseek --tools default
# 批量实验:协议 x 模型 x 夹具 x N 次 → results.csv + summary.md
scripts/run_matrix.sh -p "native-eos native-inc ctl ctl-heredoc xml" \
-m "deepseek:deepseek-flash deepseek:deepseek-v4-pro" -n 3 -r
```
口径:`pass` / `fail` / `agent_error` / `timeout` — 见 [docs/eval_tool_cst.md](docs/eval_tool_cst.md)。
## Build ## Build
### Make(Linux 默认) ### Make(Linux 默认)
```bash ```bash
cd agent cd CST-agent
make make # 依赖 pkg-config 找 libcurl / ncursesw(找不到时退回 ncurses)
make check make check # 单元测试 + 截断实验(含真实录制流)
make asan # AddressSanitizer + UBSan 构建并跑全部测试
make install PREFIX=$HOME/.local # 或 sudo make install(默认 /usr/local)
``` ```
NixOS:`nix-shell`(仓库自带 [`shell.nix`](shell.nix))后再 `make`。
macOS:`brew install curl ncurses pkg-config` 后直接 `make`(Makefile 会找 Homebrew 的 keg-only 库)。
各平台细节与验收清单见 [docs/compat.md](docs/compat.md)。
### CMake(推荐跨编译器 / 交叉) ### CMake(推荐跨编译器 / 交叉)
```bash ```bash
@@ -77,17 +100,20 @@ cmake --build build-win
# TUI:安装 PDCurses 后 -DCST_BUILD_TUI=ON -DPDCurses_ROOT=... # TUI:安装 PDCurses 后 -DCST_BUILD_TUI=ON -DPDCurses_ROOT=...
``` ```
依赖:`gcc` 或 `clang`、`libcurl`、Linux TUI 需 `ncursesw`;Windows TUI 需 **PDCurses**;sibling `../SD`(`libsashidown.a`)。 依赖:`gcc` 或 `clang`、`libcurl`、Linux TUI 需 `ncursesw`;Windows TUI 需 **PDCurses**。
API Key:env / 配置文件 / **zenity(Linux)或控制台输入(Windows / 无 zenity)**。 Sashidown(`vendor/sashidown`)与 cJSON(`vendor/cjson`,MIT)已随仓库提供。
API Key:TUI 里输入并按 provider 保存到配置目录的 `credentials`(权限 0600);`/key` 更换,`--ask-key` 在命令行输入。
`--api-key` 只用于本次进程、不落盘;`DEEPSEEK_API_KEY` 等环境变量和 `CST_API_KEY` 仅作为没有保存 key 时的兜底(方便 CI)。
## 平台支持矩阵 ## 平台支持矩阵
| OS | Arch | 编译器 | Headless | TUI | Shell | | OS | Arch | 编译器 | Headless | TUI | Shell |
|---|---|---|---|---|---| |---|---|---|---|---|---|
| Linux | x86_64 / aarch64 | GCC, Clang | yes | ncursesw | `/bin/sh` 或旁路 `busybox` | | Linux(glibc / musl) | x86_64 / aarch64 | GCC, Clang | yes | ncursesw | `/bin/sh` 或旁路 `busybox` |
| WSL2 | x86_64 | 同 Linux | yes | ncursesw | `/bin/sh`(工作区建议放在 Linux 文件系统内) |
| macOS | arm64 / x86_64 | Apple Clang | yes | ncurses(Homebrew) | `/bin/sh` |
| Android Termux | aarch64 / … | Clang (Termux) | yes | ncurses(Termux 包) | `$PREFIX/bin/sh`;可旁路 busybox | | Android Termux | aarch64 / … | Clang (Termux) | yes | ncurses(Termux 包) | `$PREFIX/bin/sh`;可旁路 busybox |
| Windows | x86_64 / aarch64 | MinGW GCC/Clang | yes | PDCurses | **捆绑 busybox-w32** | | Windows | x86_64 / aarch64 | MinGW GCC/Clang | yes | PDCurses | **捆绑 busybox-w32** |
| macOS | — | — | **不做** | — | — |
### Termux ### Termux
@@ -118,65 +144,159 @@ cmake -B build-win-tui -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64-x86_64.cmake \
## Defaults (API) ## Defaults (API)
默认 MiniMax:`https://api.minimax.cn/v1` / `MiniMax-M2.7`。 `--provider`(或 `CST_PROVIDER`)选择预设;显式的 `--api-base` / `--model` / 环境变量优先。
对接 **OpenAI 官方或其它兼容网关** 时设置:
| 用途 | 推荐变量 | 等价别名 | | provider | API Base | 默认模型 | Key 变量 |
|---|---|---| |---|---|---|---|
| Key | `OPENAI_API_KEY` | `CST_API_KEY`、`MINIMAX_API_KEY` | | `minimax`(默认) | `https://api.minimax.cn/v1` | `MiniMax-M2.7` | `MINIMAX_API_KEY` |
| Base(含 `/v1`) | `OPENAI_BASE_URL` | `CST_API_BASE`、`OPENAI_API_BASE` | | `deepseek` | `https://api.deepseek.com/v1` | `deepseek-flash`(另有 `deepseek-v4-pro`) | `DEEPSEEK_API_KEY` |
| Model | `OPENAI_MODEL` | `CST_MODEL` | | `openai` | `https://api.openai.com/v1` | `gpt-4.1-mini` | `OPENAI_API_KEY` |
| 用途 | 来源(优先级从高到低) |
|---|---|
| Provider | `--provider` → `CST_PROVIDER` → 上次使用 → `minimax` |
| Key | `--api-key` → 已保存的 key(`credentials`)→ provider 的 Key 变量 → `CST_API_KEY` → TUI 询问 |
| Base(含 `/v1`) | `--api-base` → `CST_API_BASE` → `OPENAI_BASE_URL` → `OPENAI_API_BASE` → 预设 |
| Model | `--model` → `CST_MODEL` → `OPENAI_MODEL` → 该 provider 上次使用的模型 → 预设 |
| 推理强度 | `--effort` → `CST_EFFORT` → 上次使用 → `medium` |
“上次使用”记在配置目录的 `settings.json`(TUI 里切换时更新;headless 运行不改它)。
推理强度以 `reasoning_effort` 发送给 DeepSeek 和 OpenAI 推理模型(o 系列、gpt-5,`max` 映射为 `high`),其他模型不发送。
详见 [docs/env_openai.md](docs/env_openai.md)。 详见 [docs/env_openai.md](docs/env_openai.md)。
| 项 | 默认(未设 OPENAI_* 时) | | 变量 | 作用(默认) |
|---|---| |---|---|
| API Base | `https://api.minimax.cn/v1` | | `CST_PROTOCOL` / `--protocol` | 工具调用协议(`native-eos`) |
| Model | `MiniMax-M2.7` | | `CST_TOOLS` / `--tools` | 工具集:`default`(全部)/ `minimal`(论文 4 件套) |
| API Key | env → 配置目录 credentials → zenity/控制台 | | `CST_EFFORT` / `--effort` | 推理强度 `low` / `medium` / `high` / `max` |
| `CST_CONFIRM` / `--confirm` | 哪些类别要确认:`shell,write,net`(默认)或 `none` |
环境变量:`CST_MODELS`、`CST_AUTO_APPROVE=1`、`CST_SHELL`、`CST_EXE_DIR`。 | `CST_MAX_STEPS` / `--max-steps` | 每个用户回合最多调用模型几次(30) |
| `CST_TOOL_OUT_CAP` | 单个工具输出发给模型的字节上限(16384,保留头 60% + 尾 40%) |
| `CST_CTX_WINDOW` | 发给模型的最近非 system 消息条数(80) |
| `CST_CTX_TOKENS` / `CST_AUTO_COMPACT=0` | 覆盖模型上下文窗口大小 / 关闭自动压缩(用到 80% 时自动 `/compact`) |
| `CST_NO_PROJECT_NOTES=1` | 不把工作区的 `AGENTS.md` 注入 system prompt |
| `NO_COLOR` / `CST_ASCII=1` | 关闭颜色 / 强制 ASCII 图形(非 UTF-8 终端自动退化) |
| `CST_SHELL_TIMEOUT` | `run_shell` 超时秒数(120),超时整组进程被杀,exit=124 |
| `CST_METRICS=0` / `CST_METRICS_PATH` | 关闭 / 指定计时埋点 JSONL(默认 `.agent/metrics/<session>.jsonl`) |
| `CST_RECORD_SSE=DIR` / `CST_REPLAY_SSE=DIR` | 录制 / 回放原始 SSE 流(`NNNN.sse` + `NNNN.req.json`) |
| `CST_MODELS`、`CST_AUTO_APPROVE=1`、`CST_SHELL`、`CST_EXE_DIR` | 模型列表、自动批准、shell、旁路 busybox |
## Run ## Run
```bash ```bash
# TUI(工作目录 = 当前目录;Assistant Markdown 经 Sashidown CST 渲染)
cst-agent
# Local CTL demo (no API) # Local CTL demo (no API)
./cst-agent --demo-ctl --workspace . cst-agent --demo-ctl
# TUI(OpenCode 风格;Assistant Markdown 经 Sashidown CST 渲染) # 在命令行输入并保存当前 provider 的 key(不回显)
./cst-agent --workspace .. cst-agent --ask-key --provider deepseek
# 强制重新弹窗保存 Key
./cst-agent --ask-key --workspace .
# Headless(默认 auto-approve shell/写文件) # Headless(默认 auto-approve shell/写文件)
./cst-agent --headless --prompt 'List files' --workspace . cst-agent --headless --prompt 'List files'
``` ```
### TUI 快捷键与斜杠命令 会话相关命令行参数:
| 操作 | 说明 | | 参数 | 作用 |
|---|---| |---|---|
| Enter | 发送;空输入展开最近 tool 卡;busy 时入队(最多 3) | | `-c` / `--continue` | 打开上次的会话(默认行为) |
| Ctrl+N | 输入换行(多行 composer) | | `-N` / `--new` | 开新会话 |
| ↑ / ↓ | 提示历史 | | `-R ID` / `--resume ID` | 打开指定会话(完整 id 或唯一前缀) |
| Esc / Ctrl+C | busy 时取消当前 turn | | `-L` / `--list-sessions` | 列出会话(时间、条数、标题)后退出 |
| Tab / `t`(空输入) | 展开/折叠 Thought | | `-y` / `--yolo` | 本次运行不再确认(等同 `--confirm none`) |
| 权限 | shell/写文件:`y` 允许 · `n` 拒绝 · `a` 本进程对该工具 Always | | `-C LIST` / `--confirm LIST` | 需要确认的类别:`shell,write,net` 的任意组合或 `none` |
| `/new` | 新会话 | | `-T PROFILE` / `--tools PROFILE` | `default` 或 `minimal` |
| `/session` | **可点选**会话列表(↑↓ Enter);`/session <id>` 手打切换 | | `-P NAME` / `-m NAME` / `-e LEVEL` | 本次使用的 provider / 模型 / 推理强度(不传则用上次的) |
| `/model` | **可点选**模型;`/model NAME` 直接设置(进程内) | | `-a` / `--ask-key` | 在命令行输入并保存当前 provider 的 key |
| `/compact` | 压缩上下文 |
| `/details` | 展开/折叠全部 tool 输出 |
| `/help` | 帮助 |
会话落盘:`<workspace>/.agent/sessions/<id>.jsonl` + `CURRENT`。 工作目录默认就是当前目录(`-w DIR` 可指定别的目录)。
会话落盘:`<workspace>/.agent/sessions/<id>.jsonl` + `CURRENT`;`.agent/` 自带 `.gitignore`,不会被误提交。
### 内置工具
| 工具 | 类别 | 说明 |
|---|---|---|
| `read_file` | 读 | 支持 `offset` / `limit` 分段读和行号 |
| `list_dir` | 读 | 目录树(`depth`),遵守 `.gitignore`,跳过 `.git` / `node_modules` 等 |
| `glob` | 读 | 按通配符找文件(支持 `**`) |
| `grep` | 读 | 正则搜索内容,可限定路径 / glob、忽略大小写、带上下文 |
| `write_file` | 写 | 整文件写入(自动建父目录) |
| `edit_file` | 写 | 精确片段替换(必须唯一,或 `replace_all`) |
| `apply_patch` | 写 | 应用 unified diff,多文件原子生效 |
| `delete_file` / `move_file` | 写 | 删除 / 移动(重命名)文件 |
| `run_shell` | shell | 非交互命令,超时整组进程被杀,Esc 可中断 |
| `web_fetch` | 网络 | 抓取 URL,HTML 转纯文本 |
| `todo_write` | 元 | 维护任务清单(TUI 右侧面板,Ctrl+T 开关) |
| `ask_user` | 询问 | 向用户提问(可给选项);headless 下返回错误让模型自行决定 |
`--tools minimal` 只保留 `read_file` / `run_shell` / `write_file` / `apply_patch`(论文实验口径)。
TUI 中 `/tools off NAME`、`/tools on NAME` 可临时开关单个工具。
### 权限、撤销与项目说明
- **确认**:默认 shell、写文件、联网三类工具需要确认。弹窗会显示命令或写入的 diff:
`y` 允许一次 · `a` 本工具总是允许 · `p` 总是允许这个命令前缀(如 `make`、`git status`)· `n` / Esc 拒绝。
`a` / `p` 写入 `<workspace>/.agent/permissions.json`,`/permissions` 查看,`/permissions reset` 清空。
`sudo`、`rm`、解释器(`python3 -c …`)等危险前缀不会被建议成规则。
- **撤销**:每轮写文件前自动快照到 `.agent/undo/`,`/undo` 恢复上一轮改动的全部文件(新建的文件会被删除)。
- **AGENTS.md**:工作区根目录的 `AGENTS.md` 会注入 system prompt;`/init` 让 agent 自己生成一份。
- **自动压缩**:上一轮 prompt 用到模型上下文 80% 时,下一轮开始前自动总结历史(`CST_AUTO_COMPACT=0` 关闭)。
### TUI 快捷键
| 按键 | 说明 |
|---|---|
| Enter | 发送;busy 时入队 |
| Ctrl+N / Alt+Enter | 换行(粘贴的多行文本保持原样,不会被当成发送) |
| ← → Home End Ctrl+A/E Ctrl+B/F Alt+B/F | 移动光标(按字 / 按词,支持中文宽字符) |
| Ctrl+W / Ctrl+U / Ctrl+K / Alt+Backspace / Delete | 删词 / 删到行首 / 删到行尾 |
| ↑ / ↓ | 提示历史(多行输入时先在行间移动) |
| Tab | 补全 `/命令` 和 `@文件`;空输入时展开/折叠 Thought |
| Ctrl+O / Ctrl+T | 展开最近 tool 输出 / 开关任务面板 |
| PgUp / PgDn / 鼠标滚轮 | 滚动对话 |
| Esc | 取消当前 turn(不会退出) |
| Ctrl+C 两次 / 空输入 Ctrl+D | 退出 |
### 斜杠命令
输入 `/` 弹出补全;唯一前缀即可(如 `/st` → `/status`)。标 * 的命令在模型运行中也能用。
| 命令 | 说明 |
|---|---|
| `/help` * | 命令与按键 |
| `/new`、`/session [id]`、`/rename <标题>` *、`/delete [id]` | 新建 / 选择切换 / 命名 / 删除会话 |
| `/provider [name]`、`/model [name]`、`/effort [level]` | 切换 provider(缺 key 时当场输入)、模型、推理强度;都会记住 |
| `/key` | 输入或更换当前 provider 的 API key(遮罩输入并保存) |
| `/protocol [name]` | 切换工具调用协议 |
| `/tools [on\|off NAME\|minimal\|default]` | 查看或开关工具 |
| `/permissions [reset]` *、`/yolo` *、`/max-steps [N]` * | 权限规则、本次免确认、每轮步数 |
| `/status` *、`/todo` * | 会话 / 模型 / 上下文占比 / 设置;任务清单 |
| `/undo`、`/retry` | 撤销上一轮文件改动;丢弃上一条回复重问 |
| `/diff [path]` * | `git status` + `git diff` |
| `/compact` | 手动压缩上下文 |
| `/export [file]` *、`/copy` * | 导出 Markdown 记录;复制上一条回复(OSC 52,需终端支持) |
| `/init` | 让 agent 写 `AGENTS.md` |
| `/details` *、`/quit` * | 展开全部 tool 输出;退出 |
### 协议 ### 协议
- OpenAI 兼容 `POST …/chat/completions`:原生 `tools` / SSE `tool_calls`,answer 轮 `tool_choice:none` 请求统一走 OpenAI 兼容 `POST …/chat/completions`(SSE)。工具调用的承载方式由 `--protocol` 决定:
- MiniMax XML `<minimax:tool_call>` 作 fallback
- Assistant 仅工具时 `content: null` + `tool_calls`(对齐 OpenCode/OpenAI Chat) | 协议 | 承载 | 何时执行 |
|---|---|---|
| `native-eos`(默认) | 原生 `tools[]` / SSE `tool_calls` | 整个流结束后(基线) |
| `native-inc` | 同上 | 某个 index 的参数完整(下一个 index 开始或 finish)即执行 |
| `ctl` | 正文中的 CTL 帧,长度前缀参数 | `end` 行一被确认即执行,流仍在继续 |
| `ctl-heredoc` | 正文中的 CTL 帧,`<<TAG` 定界参数 | 同上 |
| `xml` | MiniMax `<minimax:tool_call>` 文本 | 流结束后 |
文本协议(ctl / ctl-heredoc / xml)不发送 `tools[]`,工具结果以
`result <id> exit=<n> <nbytes>` … `end` 帧放在下一条 user 消息里。
同一会话可以随时切换协议:历史中的调用会按当前协议重新渲染。
## CTL tool language ## CTL tool language
@@ -184,16 +304,22 @@ cmake -B build-win-tui -DCMAKE_TOOLCHAIN_FILE=cmake/mingw-w64-x86_64.cmake \
call <id> <name> call <id> <name>
arg <key> <nbytes> arg <key> <nbytes>
<raw bytes exactly nbytes> <raw bytes exactly nbytes>
arg <key> <<TAG
<lines, verbatim>
TAG
arg <key> short inline value
end end
``` ```
Execution happens only on confirmed `end` (`CTL_EV_CALL_READY`). Execution happens only on confirmed `end` (`CTL_EV_CALL_READY`); no prefix of
a call is ever executable. See [docs/theory_tool_cst.md](docs/theory_tool_cst.md).
## Starvation experiment ## Starvation experiment
```bash ```bash
make check make check
./scripts/replay_starvation.sh ./scripts/replay_starvation.sh
./tests/test_replay_real # 真实 DeepSeek 录制流上的逐字节截断(实验 C)
``` ```
CTL vs JSON-buffer 截断数字写入 [docs/eval_tool_cst.md](docs/eval_tool_cst.md) 实验 A;与 mini_calc e2e 一起作为 Tool-loop CST 附录素材(**不**替代 SD 主论文 Markdown→HTML 实验)。 CTL vs JSON-buffer 截断数字写入 [docs/eval_tool_cst.md](docs/eval_tool_cst.md) 实验 A / C;与 mini_calc e2e 一起作为 Tool-loop CST 附录素材(**不**替代 SD 主论文 Markdown→HTML 实验)。
+94
View File
@@ -0,0 +1,94 @@
# Platform support: Linux, WSL, macOS
CST-Agent targets POSIX systems: Linux (glibc and musl), WSL 2 and macOS.
Native Windows builds are covered separately in `README-windows.md`.
## Build
| Platform | Dependencies | Build |
|---|---|---|
| Debian / Ubuntu / WSL | `build-essential pkg-config libcurl4-openssl-dev libncurses-dev` | `make` |
| Fedora | `gcc make pkgconf libcurl-devel ncurses-devel` | `make` |
| Arch | `base-devel curl ncurses` | `make` |
| Alpine (musl) | `build-base pkgconf curl-dev ncurses-dev` | `make` |
| NixOS | `nix-shell` (uses `shell.nix`) | `make` |
| macOS | `xcode-select --install`, `brew install ncurses curl pkg-config` | `make` |
`make install PREFIX=~/.local` installs the binary; CMake works too
(`cmake -B build && cmake --build build && ctest --test-dir build`).
The Makefile picks `ncursesw`, then `ncurses`, through pkg-config. On macOS it
adds Homebrew's keg-only `ncurses`/`curl` to `PKG_CONFIG_PATH`; without
Homebrew it falls back to the system `-lncurses` (wide-character capable).
Verified so far: NixOS x86_64 with gcc and clang (`-Werror`, `make check`,
`make asan`, ctest), and every source compiled `-fsyntax-only` with
`x86_64-unknown-linux-musl-gcc` without warnings. Full musl, macOS and WSL
builds run in CI (`.gitea/workflows/ci.yml`; the macOS job needs a runner and
the `HAVE_MACOS_RUNNER` variable) or by hand with the checklist below.
## Files
| What | Where |
|---|---|
| API keys (one per provider, entered in the TUI or with `--ask-key`) | `$XDG_CONFIG_HOME/cst-agent/credentials` (default `~/.config/cst-agent/`), mode 0600 |
| Last provider, models and effort | `$XDG_CONFIG_HOME/cst-agent/settings.json` |
| Sessions, undo snapshots, exports | `<workspace>/.agent/` (git-ignored automatically) |
| Saved permission rules | `<workspace>/.agent/permissions.json` |
| Project notes injected into the prompt | `<workspace>/AGENTS.md` (or `CST.md`) |
## Terminal notes
- UTF-8 locale gives the full UI. Under a non-UTF-8 locale, or with
`CST_ASCII=1`, box and icon glyphs fall back to ASCII.
- `NO_COLOR=1` turns colours off.
- `/copy` uses OSC 52. iTerm2 needs "Applications in terminal may access
clipboard"; tmux needs `set -g set-clipboard on`; Windows Terminal (WSL)
supports it out of the box.
- Bracketed paste needs ncurses (all platforms above).
- macOS Terminal.app sends Option+Enter as Esc+Enter only with "Use Option as
Meta key"; Ctrl+N always inserts a newline.
## Acceptance checklist
Run on each platform before a release. Use a scratch git repository as the
workspace and a real API key (entered when the TUI asks for it).
Build and tests:
- [ ] `make` has no warnings; `make check` passes
- [ ] `make asan` passes (Linux; macOS: `make CFLAGS="-fsanitize=address,undefined -g" check`)
- [ ] `cmake -B build && cmake --build build && ctest --test-dir build` passes
- [ ] `make install PREFIX=/tmp/cst && /tmp/cst/bin/cst-agent --help`
CLI:
- [ ] `--list-sessions`, `--new`, `--resume <prefix>`, `--continue` behave as documented
- [ ] `--headless --prompt "list the files"` prints a transcript and exits 0
- [ ] First start asks for a provider and key; the credentials file is mode 0600
- [ ] Second start reuses provider, model and effort without asking
TUI:
- [ ] Typing Chinese/emoji keeps the cursor in the right column; ←→ Home End work
- [ ] Pasting a multi-line block does not send it; Enter sends
- [ ] `/` shows the command popup, Tab completes, `@` completes file paths
- [ ] Esc cancels a running turn; Esc on an empty prompt does nothing; Ctrl+C twice quits
- [ ] A long `run_shell` (e.g. `sleep 30`) stops promptly on Esc
- [ ] Write permission shows the diff; `a` saves a rule; `p` saves a shell prefix rule
- [ ] `ask_user` modal accepts a number, an arrow selection and typed text
- [ ] Todo panel appears while tasks are open; Ctrl+T hides it
- [ ] `/undo` restores files changed by the last turn; `/retry` re-asks
- [ ] `/diff`, `/export`, `/status`, `/tools off grep`, `/yolo` work
- [ ] Resize the terminal during streaming: layout recovers
- [ ] `NO_COLOR=1` and `CST_ASCII=1` render readable screens
WSL specifics:
- [ ] Workspace under `/mnt/c/...` works (slower IO is expected); `~/project` is recommended
- [ ] Windows Terminal: mouse wheel scroll and OSC 52 `/copy`
macOS specifics:
- [ ] Builds with Homebrew ncurses and with the system ncurses (`brew unlink ncurses`)
- [ ] Terminal.app and iTerm2: arrows, Home/End, PgUp/PgDn, bracketed paste
+50 -28
View File
@@ -1,47 +1,56 @@
# OpenAI 兼容 API 环境变量 # OpenAI 兼容 API 环境变量
CST-Agent 使用 **OpenAI Chat Completions** 协议(`POST {base}/chat/completions`), CST-Agent 使用 **OpenAI Chat Completions** 协议(`POST {base}/chat/completions`,SSE 流式),
可对接官方 OpenAI、MiniMax、以及其它兼容网关。 可对接官方 OpenAI、DeepSeek、MiniMax,以及其它兼容网关。
## 必填 / 常用 ## Provider 预设
| 变量 | 别名 | 说明 | 示例 | `--provider NAME`(或 `CST_PROVIDER`,或 TUI 里的 `/provider`)一次性给出 base 和默认模型。
不传时沿用上次使用的 provider(记在配置目录的 `settings.json`)。
| provider | API Base | 默认模型 | Key 变量 |
|---|---|---|---| |---|---|---|---|
| `CST_API_KEY` | `OPENAI_API_KEY`、`MINIMAX_API_KEY` | API Key(按此优先级读取) | `sk-...` | | `minimax`(默认) | `https://api.minimax.cn/v1` | `MiniMax-M2.7` | `MINIMAX_API_KEY` |
| `CST_API_BASE` | `OPENAI_BASE_URL`、`OPENAI_API_BASE` | API 根路径,须含 `/v1`(不要带 `/chat/completions`) | `https://api.openai.com/v1` | | `deepseek` | `https://api.deepseek.com/v1` | `deepseek-flash`(另有 `deepseek-v4-pro`) | `DEEPSEEK_API_KEY` |
| `CST_MODEL` | `OPENAI_MODEL` | 模型名 | `gpt-4.1-mini` / `MiniMax-M2.7` | | `openai` | `https://api.openai.com/v1` | `gpt-4.1-mini` | `OPENAI_API_KEY` |
未设置 key 时:读配置文件 →(Linux)zenity / 控制台输入。 显式给出的 `--api-base` / `--model` / 下表中的环境变量优先于预设。
配置文件: ## 解析顺序
- Linux/Termux:`~/.config/cst-agent/credentials`
| 项 | 优先级(高 → 低) |
|---|---|
| Key | `--api-key`(**只用于本次进程,不写盘**)→ 该 provider 已保存的 key → provider 的 Key 变量 → `CST_API_KEY` → TUI 弹出遮罩输入框(输入后保存) |
| Base(含 `/v1`,不带 `/chat/completions`) | `--api-base` → `CST_API_BASE` → `OPENAI_BASE_URL` → `OPENAI_API_BASE` → 预设 |
| Model | `--model` → `CST_MODEL` → `OPENAI_MODEL` → 该 provider 上次使用的模型 → 预设 |
| 推理强度 | `--effort` → `CST_EFFORT` → 上次使用 → `medium`(low / medium / high / max) |
key 按 provider 保存在 credentials 文件(权限 0600),每行 `<provider>.api_key=...`;
TUI 里 `/key` 更换,命令行 `--ask-key` 输入(不回显):
- Linux/macOS/Termux:`$XDG_CONFIG_HOME/cst-agent/credentials`(默认 `~/.config/cst-agent/`)
- Windows:`%APPDATA%\cst-agent\credentials` - Windows:`%APPDATA%\cst-agent\credentials`
旧版只有一行 `api_key=` 的文件仍可读:按其中的 `api_base` 归到对应 provider(没有 `api_base` 时视为 MiniMax)。
## 示例 ## 示例
### DeepSeek
```bash
cst-agent --provider deepseek # 首次会询问 key;默认 deepseek-flash
cst-agent --provider deepseek --model deepseek-v4-pro -e high # 推理内容显示为 Thought
```
### OpenAI 官方 ### OpenAI 官方
```bash ```bash
export OPENAI_API_KEY='sk-...' cst-agent --provider openai
export OPENAI_BASE_URL='https://api.openai.com/v1'
export OPENAI_MODEL='gpt-4.1-mini'
./cst-agent --workspace .
```
等价 CST 前缀:
```bash
export CST_API_KEY='sk-...'
export CST_API_BASE='https://api.openai.com/v1'
export CST_MODEL='gpt-4.1-mini'
``` ```
### MiniMax(默认) ### MiniMax(默认)
```bash ```bash
export CST_API_KEY='...' # 或 MINIMAX_API_KEY cst-agent --provider minimax
# 默认 base=https://api.minimax.cn/v1 model=MiniMax-M2.7
./cst-agent --workspace .
``` ```
### 任意兼容网关 ### 任意兼容网关
@@ -53,21 +62,34 @@ export OPENAI_MODEL='your-model'
./cst-agent --headless --prompt 'ping' --workspace . ./cst-agent --headless --prompt 'ping' --workspace .
``` ```
命令行也可覆盖:`--api-base` / `--api-key` / `--model`。 网关不支持 `stream_options.include_usage` 时设 `CST_NO_STREAM_USAGE=1`。
## 其它环境变量 ## 其它环境变量
| 变量 | 说明 | | 变量 | 说明 |
|---|---| |---|---|
| `CST_PROTOCOL` | 工具调用协议:`native-eos`(默认)/ `native-inc` / `ctl` / `ctl-heredoc` / `xml` |
| `CST_MAX_STEPS` | 每个用户回合最多调用模型几次(默认 30) |
| `CST_TOOL_OUT_CAP` | 单个工具输出发给模型的字节上限(默认 16384) |
| `CST_CTX_WINDOW` | 发给模型的最近非 system 消息条数(默认 80) |
| `CST_SHELL_TIMEOUT` | `run_shell` 超时秒数(默认 120) |
| `CST_METRICS=0` / `CST_METRICS_PATH` | 关闭 / 指定计时埋点 JSONL |
| `CST_RECORD_SSE` / `CST_REPLAY_SSE` | 录制 / 回放原始 SSE 流目录;`CST_REPLAY_CHUNK` 控制回放分块字节数 |
| `CST_MODELS` | `/model` 可选列表,`:` 或 `,` 分隔 | | `CST_MODELS` | `/model` 可选列表,`:` 或 `,` 分隔 |
| `CST_AUTO_APPROVE=1` | 自动允许 shell/写文件(headless 常用) | | `CST_AUTO_APPROVE=1` | 自动允许 shell/写文件(headless 默认开启) |
| `CST_SHELL` | shell 解释器路径(覆盖 busybox/`sh`) | | `CST_SHELL` | shell 解释器路径(覆盖 busybox/`sh`) |
| `CST_EXE_DIR` | 可执行文件目录(Termux 旁路 busybox 时有用) | | `CST_EXE_DIR` | 可执行文件目录(Termux 旁路 busybox 时有用) |
网络:连接超时 30 秒;300 秒内无数据视为超时;429 / 5xx / 连接错误在尚未收到任何内容时自动重试 3 次(1s、2s、4s 退避)。
## CLI 对照 ## CLI 对照
```text ```text
--provider NAME ← CST_PROVIDER
--api-base URL ← CST_API_BASE / OPENAI_BASE_URL --api-base URL ← CST_API_BASE / OPENAI_BASE_URL
--api-key KEY ← CST_API_KEY / OPENAI_API_KEY --api-key KEY ← 已保存的 key / <provider key 变量> / CST_API_KEY
--model NAME ← CST_MODEL / OPENAI_MODEL --model NAME ← CST_MODEL / OPENAI_MODEL
--effort LEVEL ← CST_EFFORT
--protocol P ← CST_PROTOCOL
--max-steps N ← CST_MAX_STEPS
``` ```
+101 -22
View File
@@ -10,18 +10,15 @@
| 层 | 主张 | 主变量 | 非主张 | | 层 | 主张 | 主变量 | 非主张 |
|---|---|---|---| |---|---|---|---|
| 论文 CST | \(O_{\mathrm{conf}}\) 有界确认 → HTML | \(\mathcal{L}_0\) vs 引用式链接等 | — | | 论文 CST | \(O_{\mathrm{conf}}\) 有界确认 → HTML | \(\mathcal{L}_0\) vs 引用式链接等 | — |
| Tool-loop CST | 同构到工具帧 | **CTL vs JSON-buffer** 截断 | 网关空答率、模型智商 | | Tool-loop CST | 同构到工具帧 | **CTL vs JSON-buffer** 截断;确认即执行 | 网关空答率、模型智商 |
| 工程载体 | OpenAI `tool_calls` + XML fallback | 可演示端到端 | 不算进 CST 正确性定理 | | 工程载体 | 五种协议同一 agent 循环 | 端到端通过率、步数、token、时延 | 不算进 CST 正确性定理 |
## 实验 A — 饥饿 / 截断(已自动化) ## 实验 A — 饥饿 / 截断(合成样例,已自动化)
```bash ```bash
cd agent && make check make check # 关注 tests/test_replay_starvation
# 关注 ./tests/test_replay_starvation
``` ```
填写模板(跑完后更新数字):
| 指标 | CTL | JSON-buffer | | 指标 | CTL | JSON-buffer |
|---|---|---| |---|---|---|
| 完整输入可执行 | yes | yes | | 完整输入可执行 | yes | yes |
@@ -29,45 +26,127 @@ cd agent && make check
| 安全截断样本(本机 `make check`) | 3 | 3 次 trunc failure | | 安全截断样本(本机 `make check`) | 3 | 3 次 trunc failure |
对应理论:裸 JSON ∉ \(\mathcal{L}_0^{\mathrm{tool}}\);CTL 长度前缀使参数体字节可确认,pending 有界。 对应理论:裸 JSON ∉ \(\mathcal{L}_0^{\mathrm{tool}}\);CTL 长度前缀使参数体字节可确认,pending 有界。
定界写法另有 `tests/test_ctl_heredoc`:对一个含 TAB、空行、`end`/`call`/围栏行和缩进 TAG 的体,
在**每一个**字节前缀上都不产生 `CALL_READY`,完整流上体逐字节还原。
## 实验 B — mini_calc 端到端案例(不可逆发射) ## 实验 B — mini_calc 端到端案例(不可逆发射)
协议见 [fixtures/mini_calc/](../fixtures/mini_calc/) 与 [scripts/e2e_mini_calc.sh](../scripts/e2e_mini_calc.sh)。 协议见 [fixtures/mini_calc/](../fixtures/mini_calc/) 与 [scripts/e2e_fixture.sh](../scripts/e2e_fixture.sh)。
```bash ```bash
cd agent ./scripts/e2e_mini_calc.sh --baseline-only # 无 key:证明 fixture 初始 RED
./scripts/e2e_mini_calc.sh --baseline-only # 无 key:证明 fixture 初始 RED ./scripts/e2e_mini_calc.sh --provider deepseek # 有 key:agent 修 bug → make test
./scripts/e2e_mini_calc.sh # 有 key:agent 修 bug → make test
``` ```
### 验收口径 ### 验收口径
每个夹具有 `TASK.md`(任务书)和 `check.sh`(判定脚本)。基线在一份临时副本上先跑 `check.sh`,必须失败。
| outcome | 含义 | | outcome | 含义 |
|---|---| |---|---|
| `pass` | `make test` 绿 | | `pass` | agent 结束后 `check.sh` 通过 |
| `partial` | 改了 `calc.c` 但测试仍红 | | `fail` | agent 正常结束但 `check.sh` 失败 |
| `fail` | 未改文件或 agent/API 失败 | | `agent_error` | agent 非零退出(如连续契约错误、API 错误) |
| `baseline_ok_agent_skipped` / `baseline_ok_no_api_key` | 仅验证夹具 | | `timeout` | 超过 `E2E_TIMEOUT`(默认 900 秒) |
| `unexpected_green_fixture` | 夹具一开始就通过(夹具坏了) |
### 论文可引用的叙事 ### 论文可引用的叙事
1. Session JSONL 中顺序:assistant `tool_calls` → `role:tool` 结果 →(可选)写文件/shell → 最终文本。 1. Session JSONL 中顺序:assistant 调用 → 工具结果 → 最终文本;只追加。
2. 工具仅在确认/原生完整 arguments 后执行(工程桥);**CTL 饥饿实验**仍是形式主证据。 2. 工具仅在确认(CTL `end`、原生参数完整)后执行;**CTL 饥饿实验**仍是形式主证据。
3. 案例展示「确认后不可撤回的磁盘副作用」与 transcript 只追加。 3. 案例展示「确认后不可撤回的磁盘副作用」与 transcript 只追加。
### 跑分记录(模板) ### 稳定性(M1,DeepSeek `deepseek-chat`,ASAN + UBSan 构建)
| 日期 (UTC) | model | outcome | notes | | 日期 (UTC) | 场景 | 结果 |
|---|---|---|---| |---|---|---|
| 2026-09-07 | MiniMax-M2.7 | **pass** | `e2e_mini_calc.sh`;baseline RED → agent 修 `add`/`div_ints` → `make test` 绿;见 `out/result-20260907T131300Z.txt` | | 2026-09-07 | mini_calc,MiniMax-M2.7 | **pass**(旧版本,`out/result-20260907T131300Z.txt`) |
| 2026-10-04 | mini_calc 连续 10 次,native-eos | **10/10 pass**,ASAN/LSan 无报告 |
| 2026-10-04 | makefile_tab(写含 TAB 的 Makefile;旧版本此处 HTTP 400) | pass |
| 2026-10-04 | big_file(写 5000 字节文件;旧版本长内容被截断) | pass |
| 2026-10-04 | test_fix_test(先跑测试→修→再跑;旧版本第二次 `make test` 被错误去重) | pass,`make test` 实际执行 2 次 |
| 2026-10-04 | mini_calc,`ctl` / `ctl-heredoc` / `native-inc` / `xml` 各 1 次(ASAN) | 4/4 pass |
## 实验 C — 真实模型流上的逐字节截断
```bash
make check # 关注 tests/test_replay_real
```
数据:`tests/fixtures/sse/deepseek-*/NNNN.sse`,是 `CST_RECORD_SSE` 录下的 DeepSeek 原始 SSE 流(同一任务分别用三种协议)。
测试把每条助手消息还原成运行时看到的逻辑流,在**每一个字节**处截断:CTL 流喂给 CTL 解析器;原生流把各调用拼成
`[{"name","arguments"},…]`,分别按 json-whole(整体可解析才执行,即 native-eos)和 json-object(每个对象可解析即执行,即 native-inc)计数。
全部前缀上 S1/S2/S3 成立(未完整调用不可执行;执行过的调用与完整流一致;可执行数单调)。可执行调用数随前缀比例变化:
| 消息 | 协议 | 字节 | 调用 | 25% | 50% | 75% | 90% | 100% | 首个调用可执行于 |
|---|---|---|---|---|---|---|---|---|---|
| heredoc #0 | ctl-heredoc | 193 | 3 | 0 | 1 | 2 | 2 | 3 | 45% |
| heredoc #1 | ctl-heredoc | 223 | 2 | 0 | 0 | 0 | 1 | 2 | 85% |
| ctl #0 | ctl | 74 | 2 | 0 | 0 | 1 | 1 | 2 | 51% |
| native #0 | json-whole | 100 | 2 | 0 | 0 | 0 | 0 | 2 | 100% |
| native #0 | json-object | 100 | 2 | 0 | 0 | 1 | 1 | 2 | — |
| native #1 | json-whole | 108 | 2 | 0 | 0 | 0 | 0 | 2 | 100% |
| native #1 | json-object | 108 | 2 | 0 | 1 | 1 | 1 | 2 | — |
读法:json-whole 在流结束前一个调用都不能执行;CTL 与 json-object 都是前缀单调的,确认点落在每个调用结束处。
CTL 的区别在于 pending 有界(\(\le L_{\mathrm{line}}\)),json-object 的 pending 是整个参数对象。
另外 `ctl` 目录里 #1–#4 是四次长度前缀写错的消息(三次 patch 字节数写大、一次 content 写小,见实验 D),解析器在这些消息的任何前缀上**一次也没有执行**。
`tests/test_sse_decode` 还验证:录制流按 1 字节和 4096 字节分块回放,解码出的文本、调用和参数完全相同(网络分块不影响确认结果)。
## 实验 D — 协议对照矩阵(M3)
```bash
scripts/run_matrix.sh -p "native-eos native-inc ctl ctl-heredoc xml" \
-m "deepseek:deepseek-flash" -f "mini_calc makefile_tab big_file test_fix_test" -n 3 -r
# → out/matrix-*/results.csv(每次运行一行)与 summary.md
```
2026-10-04,DeepSeek `deepseek-chat`,5 协议 × 4 夹具 × 3 次 = 60 次(`out/matrix-m3/`):
| 协议 | n | 通过率 | 步数 | 墙钟 s | 模型时间 s | 工具时间 s | wait_ms | 契约错误/次 | 输入 token | 输出 token |
|---|---|---|---|---|---|---|---|---|---|---|
| native-eos | 12 | 100% | 4.2 | 5.38 | 5.11 | 0.24 | 30 | 0 | 9900 | 1058 |
| native-inc | 12 | 100% | 4.7 | 5.90 | 5.82 | 0.26 | 0 | 0 | 10615 | 1084 |
| ctl | 12 | 58% | 5.8 | 7.26 | 7.09 | 0.16 | 0 | 2.33 | 10574 | 1336 |
| **ctl-heredoc** | 12 | **100%** | **3.7** | **4.48** | 4.27 | 0.24 | 0 | **0** | **5728** | **756** |
| xml | 12 | 92% | 5.7 | 7.09 | 6.84 | 0.19 | 53 | 1.75 | 10468 | 1398 |
`wait_ms` = 调用确认到开始执行的平均间隔。eager 协议恒为 0;流结束后执行的协议要等到整条消息结束。
契约错误分解(全部 12 次运行合计):
| 协议 | 参数写法使用次数 | 契约错误 |
|---|---|---|
| ctl | 长度前缀 33、行内 73 | `unexpected_line`(数少了)20、`body_eof`(数多了)8 |
| ctl-heredoc | 定界 30、行内 29、长度前缀 0 | 0 |
| xml | — | 标签写坏(如 `<minimax:tool_call">`)21 |
结论(可写入论文):
1. **长度前缀对真实模型不可靠**。模型几乎从不数对多行参数的字节数(33 次长度前缀参数里 28 次契约错误),
ctl 只有 58% 通过,失败的运行都是连续契约错误后放弃。CST 的安全性仍然成立——错误帧从未被执行——但可用性差。
2. **定界写法消除了计数负担**,保留同样的确认前沿与有界 pending:ctl-heredoc 100% 通过、0 契约错误,
且步数、墙钟、token 都是五种协议里最少的(输入 token 比 native-eos 少 42%:不发 `tools[]` schema,参数也不需要 JSON 转义)。
3. **eager 执行在这些任务上的时延收益很小**:确认到执行的等待从几十毫秒降到 0;墙钟差异主要来自步数和 token,而非执行时机。
原因是模型通常把耗时工具(make、测试)放在一条消息的最后一个调用,确认之后流几乎立即结束。
4. **反馈让文本协议可恢复**:xml 在加入“标签写坏→契约错误→要求重发”之前为 58%(`out/matrix-m3-old-xml/`),之后为 92%(重复一组为 83%,`out/matrix-m3-xml-replicate/`)。
注意事项:
- 执行在 SSE 回调内同步进行,执行期间客户端暂停读流,所以客户端测得的“流结束”包含了工具时间。
`summary.md` 里的 `overlap_s` 因此是上界(native-inc 下恒等于工具时间),不能当作节省的时间引用。
- 每格只有 3 次,模型输出有随机性;引用通过率时请同时给出 n。
## 与主论文的衔接建议 ## 与主论文的衔接建议
- **附录 / 扩展节标题示例**:「Tool-loop CST:确认前沿到工具调用」 - **附录 / 扩展节标题示例**:「Tool-loop CST:确认前沿到工具调用」
- 粘贴:theory 表(theory_tool_cst.md)+ 实验 A 表 + 实验 B 一行案例。 - 粘贴:theory 表(theory_tool_cst.md)+ 实验 A/C 表 + 实验 D 主表与结论 1–2。
- 明确声明:主 fan-out / \(\mathcal{A}=0\) 数字仍来自 Sashidown;本笔记只提供 **同构第二实例**。 - 明确声明:主 fan-out / \(\mathcal{A}=0\) 数字仍来自 Sashidown;本笔记只提供 **同构第二实例**。
## 明确不写进定理的内容 ## 明确不写进定理的内容
- 商业 API 空回复、权限 UX、TUI 观感 - 商业 API 空回复、权限 UX、TUI 观感
- 「已达生产 coding agent」类宣称 - 「已达生产 coding agent」类宣称
- 实验 D 的时延数字(受网络与服务端负载影响)
+67
View File
@@ -43,6 +43,29 @@ end\n
取 \(K = L_{\mathrm{line}}\)(行头)与实现常量 `CTL_MAX_ARG`(单参上限,契约拒绝更大值)。 取 \(K = L_{\mathrm{line}}\)(行头)与实现常量 `CTL_MAX_ARG`(单参上限,契约拒绝更大值)。
### 参数的三种写法
| 写法 | 语法 | 何时确认 | 代价 |
|---|---|---|---|
| 长度前缀 | `arg <key> <nbytes>` + 恰好 nbytes 字节 | 每个体字节到达即确认;`end\n` 确认整次调用 | 模型必须**精确数字节**(含换行、UTF-8 多字节) |
| 行内 | `arg <key> <value…>`(值为行尾剩余部分) | 头行 `\n` 即确认 | 只能单行;值不能全是数字(否则视为长度前缀) |
| 定界(heredoc) | `arg <key> <<TAG` … 单独一行 `TAG` | 每个完整体行(`\n` 到达且 ≠ TAG)即确认 | 体不能含独立成行的 TAG;值总以换行结尾;单行 ≤ \(L_{\mathrm{line}}\) |
定界写法的确认规则:
1. 头行 `arg <key> <<TAG\n` 确认 `(key, TAG)`;TAG 须匹配 `[A-Za-z0-9_]{1,32}`,可加单/双引号。
2. 体内每读到一个 `\n`:若该行**逐字节等于** TAG,则参数结束;否则该行(连同换行)确认为体的一部分。行首空白、`end`、`call`、代码围栏在体内都只是普通文本。
3. 未闭合行的 pending \(\le L_{\mathrm{line}}\):它可能成为 TAG,也可能是体行,在 `\n` 前不能确认——与行头同界,所以 \(K = L_{\mathrm{line}}\) 不变。
4. 流在 TAG 前结束 → `heredoc_eof` 契约错误,不执行。最后一行恰为 TAG 且无换行时,EOF 视为该行结束(EOF 充分性)。
定界写法与长度前缀的确认前沿完全相同(都是“参数完整后 `end\n`”),差别只在**谁承担边界信息**:长度前缀要求模型事先给出精确计数(真实模型常数错,见 eval 实验 D),定界写法把边界放到流里,由运行时逐行检测。
### 契约错误的分类
解析器在离开 \(\mathcal{L}_0^{\mathrm{tool}}\) 时给出 `ctl_error_kind_t`,供实验统计:
`bad_call`、`bad_arg`、`unexpected_line`(长度前缀写小了,体后面还有内容)、`body_eof`(写大了,流先结束)、`heredoc_eof`、`line_too_long`、`limit`。
错误信息会带上声明值与实际到达字节数(例如 `declared 495 bytes but only 438 arrived`),运行时把它作为下一条 user 消息反馈给模型重发;已确认并执行的调用不受影响。
## 不可逆发射 ## 不可逆发射
实现是确认忠实的,当且仅当: 实现是确认忠实的,当且仅当:
@@ -55,6 +78,50 @@ end\n
**JSON-buffer**:将模型输出拼接到可 `cJSON_Parse` 的 `{"name","arguments"}`(或 OpenAI `tool_calls` 形)才执行;流中途截断则失败。与 CTL 使用同一截断点集合,只换帧语法。 **JSON-buffer**:将模型输出拼接到可 `cJSON_Parse` 的 `{"name","arguments"}`(或 OpenAI `tool_calls` 形)才执行;流中途截断则失败。与 CTL 使用同一截断点集合,只换帧语法。
细分两档(实验 C 都测):
- **json-whole**:整条消息的 `tool_calls` 数组可解析才执行(= 原生接口流结束后执行,`native-eos`)。
- **json-object**:每个调用对象一旦可解析就执行(= `native-inc`)。它也是前缀单调的,但 pending 是“当前调用的全部参数”,不受 \(L_{\mathrm{line}}\) 约束:一个 5KB 的 `content` 字段在最后一个 `"}` 到达前一个字节都不能确认。
## 运行时:五种协议模式
同一个 agent 循环支持五种承载方式(`--protocol`),只换“调用从哪里来、何时执行”,工具、会话、权限完全相同,便于做对照实验。
| 模式 | 请求 | 调用来源 | 执行时刻(不可逆点) |
|---|---|---|---|
| `native-eos` | 发 `tools[]` | SSE `tool_calls` | 流结束后统一执行 |
| `native-inc` | 发 `tools[]` | SSE `tool_calls` | 某 index 的参数完整(出现下一个 index / `finish_reason` / `[DONE]`),且 JSON 可解析 |
| `ctl` | 不发 `tools[]` | 正文 CTL 帧(长度前缀) | `CALL_READY`(`end\n` 确认)即执行,流继续 |
| `ctl-heredoc` | 同上 | 正文 CTL 帧(定界) | 同上 |
| `xml` | 同上 | MiniMax `<minimax:tool_call>` | 流结束后执行 |
文本协议的结果帧:
```text
result <id> exit=<code> <nbytes>\n
<nbytes 字节输出>\n
end\n
```
作为下一条 user 消息发送(模型不得自己写 result 帧)。超过 `CST_TOOL_OUT_CAP` 的输出先按“头 60% + 尾 40%”截断再计算 nbytes,保证帧内字节数精确。
### eager 执行的不可逆语义
- 调用在确认时刻立刻执行(同步,在 SSE 回调内)。执行期间客户端暂停读流,但服务端仍在继续生成,字节堆在 socket 缓冲里,工具返回后再被消费——模型生成与工具执行因此重叠。多个调用严格按确认顺序执行。
- CTL 模式下,**已确认调用之后**若模型继续写普通文本(例如“臆想”工具结果),运行时立即停止读流(客户端 stop,而非错误),只把截至最后一个确认调用的原始文本写入会话。这正是“确认前沿之外的字节不产生副作用”。
- 若同一条消息里,已执行的调用之后出现契约错误:已执行的副作用保留并记录,错误帧之后的内容丢弃,并请模型重发。
- 用户取消(Esc)在执行中的调用上表现为“进程组被终止(exit=130)”,仍然记录,不撤回。
### 实测检验的三条性质(实验 C)
对真实录制流的每一个字节前缀:
- **S1**:未完整的调用永远不可执行;
- **S2**:前缀上已执行的调用与完整流中的同一调用逐字节相同(执行永不被撤回或改写);
- **S3**:可执行调用数随前缀长度单调不减。
## 补丁确认 ## 补丁确认
`apply_patch` 的 patch 文本作为 CTL 的一个 `arg`;应用侧再要求 unified diff 的每个 hunk 完整(`@@` 到下一块或 EOF)才写盘——整 hunk 确认,禁止半截应用。 `apply_patch` 的 patch 文本作为 CTL 的一个 `arg`;应用侧再要求 unified diff 的每个 hunk 完整(`@@` 到下一块或 EOF)才写盘——整 hunk 确认,禁止半截应用。
实现上更进一步:一个 patch 可含多个文件(`---`/`+++` 头,`/dev/null` 表示新建/删除)。所有文件的所有 hunk 先在内存中定位并计算结果,**全部成功才写盘**;任一 hunk 定位失败或有歧义(同一上下文匹配多处且无行号),整个 patch 不产生任何磁盘副作用。hunk 优先在 `@@ -a,b` 给出的行号附近查找,行号不准时退化为搜索,并容忍行尾空白差异。
+20
View File
@@ -0,0 +1,20 @@
# Task: three small scripts, planned with a task list
Create these files in the workspace root:
1. `greet.sh` — prints `Hello, NAME!` where NAME is the first argument, or
`Hello, world!` when no argument is given.
2. `count.sh` — prints the number of lines in the file given as the first
argument (just the number). Exits with status 2 and prints a usage line to
stderr when no argument is given.
3. `SCRIPTS.md` — one short section per script explaining how to run it.
Both scripts must be executable POSIX shell scripts (`#!/bin/sh`).
Rules:
- Start by calling `todo_write` with one item per file, then keep the list
up to date (mark each item done as you finish it). Every item must be done
at the end.
- Nobody is available to answer questions in this run. If `ask_user` fails,
pick a sensible default and continue.
- Test both scripts with `run_shell` before you finish.
+34
View File
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# Pass iff the scripts work and the agent kept a finished task list.
set -euo pipefail
cd "$(dirname "$0")"
fail() { echo "check: $*"; exit 1; }
[[ -x greet.sh && -x count.sh ]] || fail "scripts missing or not executable"
[[ "$(./greet.sh)" == "Hello, world!" ]] || fail "greet.sh without argument"
[[ "$(./greet.sh Ada)" == "Hello, Ada!" ]] || fail "greet.sh Ada"
[[ "$(./count.sh notes.txt | tr -d ' ')" == "3" ]] || fail "count.sh notes.txt"
set +e
./count.sh >/dev/null 2>&1
rc=$?
set -e
[[ "$rc" -eq 2 ]] || fail "count.sh without argument should exit 2 (got $rc)"
[[ -s SCRIPTS.md ]] || fail "SCRIPTS.md missing"
grep -q greet.sh SCRIPTS.md && grep -q count.sh SCRIPTS.md ||
fail "SCRIPTS.md does not mention both scripts"
# The session must show todo_write ending with every item done.
python3 - <<'PY' || fail "no finished todo_write list in the session"
import glob, json, re, sys
last = None
for path in glob.glob(".agent/*.jsonl") + glob.glob(".agent/sessions/*.jsonl"):
for line in open(path, encoding="utf-8"):
try:
m = json.loads(line)
except ValueError:
continue
c = m.get("content") or ""
r = re.search(r"todo list updated: (\d+)/(\d+) done", c)
if r:
last = (int(r.group(1)), int(r.group(2)))
sys.exit(0 if last and last[1] >= 3 and last[0] == last[1] else 1)
PY
echo "check: PASSED"
+3
View File
@@ -0,0 +1,3 @@
first
second
third
+5
View File
@@ -0,0 +1,5 @@
.PHONY: check
check:
@test -f lines.txt || { echo "lines.txt missing"; exit 1; }
@seq -f 'line %04g' 1 500 | cmp - lines.txt && echo "check: PASSED ($$(wc -c < lines.txt) bytes)"
+8
View File
@@ -0,0 +1,8 @@
# 任务:写一个大于 4KB 的文件
请用 `write_file` 工具(不要用 shell 的 seq/printf/循环生成)一次性写出 `lines.txt`:
- 共 500 行,第 i 行是 `line ` 加上 4 位补零的 i,例如第 1 行 `line 0001`,第 500 行 `line 0500`;
- 每行以换行结尾,文件总长 5000 字节。
然后运行 `make check` 验证;若失败,读输出修正后再验证。最后用一句话说明结果。
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
make check
+11
View File
@@ -0,0 +1,11 @@
# 任务:为 hello 项目写 Makefile
工作区里只有 `hello.c` 和 `tests/expect.txt`,还没有 Makefile。请:
1. 新建 `Makefile`(注意:命令行必须用 TAB 缩进),包含以下目标:
- `all`(默认目标):用 `cc -std=c11 -Wall -Wextra -O2 -o hello hello.c` 编译出 `hello`;
- `test`:依赖 `hello`,运行 `./hello > tests/actual.txt`,再用 `diff -u tests/expect.txt tests/actual.txt` 比较;
- `clean`:删除 `hello` 和 `tests/actual.txt`;
- 把 `all test clean` 声明为 `.PHONY`。
2. 运行 `make clean && make test`,确认通过。
3. 用一句话说明结果。
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/env bash
# Pass iff the agent wrote a working Makefile (recipes need real TABs).
set -euo pipefail
cd "$(dirname "$0")"
test -f Makefile
grep -q "$(printf '^\t')" Makefile
make clean >/dev/null
make test
make clean >/dev/null
test ! -e hello
+7
View File
@@ -0,0 +1,7 @@
#include <stdio.h>
int main(void) {
printf("hello, cst\n");
printf("tab\there\n");
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
hello, cst
tab here
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
# Pass iff the task is done (used by scripts/e2e_fixture.sh).
set -euo pipefail
cd "$(dirname "$0")"
make test
+20
View File
@@ -0,0 +1,20 @@
CC ?= gcc
CFLAGS ?= -std=c11 -Wall -Wextra -O2
OBJS = main.o order.o report.o
.PHONY: all clean test
all: shop
shop: $(OBJS)
$(CC) $(CFLAGS) -o $@ $(OBJS)
%.o: %.c order.h report.h
$(CC) $(CFLAGS) -c -o $@ $<
test: shop
./tests/run.sh
clean:
rm -f shop $(OBJS)
+15
View File
@@ -0,0 +1,15 @@
# Task: rename across a small C project
`shop` is a tiny C program split over several files. Do the following, then stop:
1. Rename the function `compute_total` to `order_total` everywhere: the
declaration, the definition, every call site and the comments. Nothing
named `compute_total` may remain anywhere in the project.
2. `report.c` applies the 10% discount a second time, so the printed total is
too low. Fix it so the discount is applied exactly once (`order_total`
already applies it).
3. Run `make test` and make sure it passes.
4. Summarize the change in two or three sentences.
Find the call sites with the search tools instead of guessing. Edit only
what the task needs.
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
# Pass iff the rename is complete and the discount bug is fixed.
set -euo pipefail
cd "$(dirname "$0")"
if grep -rn --include='*.c' --include='*.h' compute_total . ; then
echo "check: compute_total still present"
exit 1
fi
grep -q 'int order_total(const int \*prices, int n);' order.h
make -s clean >/dev/null
make -s test
+19
View File
@@ -0,0 +1,19 @@
#include <stdio.h>
#include <stdlib.h>
#include "order.h"
#include "report.h"
int main(int argc, char **argv) {
int prices[64];
int n = 0;
for (int i = 1; i < argc && n < 64; i++)
prices[n++] = atoi(argv[i]);
if (n == 0) {
fprintf(stderr, "usage: shop PRICE_CENTS...\n");
return 2;
}
print_report(stdout, prices, n);
/* exit status: 0 when compute_total is positive */
return compute_total(prices, n) > 0 ? 0 : 1;
}
+17
View File
@@ -0,0 +1,17 @@
#include "order.h"
int compute_total(const int *prices, int n) {
int sum = 0;
for (int i = 0; i < n; i++)
sum += prices[i];
if (n > 3)
sum -= sum / 10; /* bulk discount */
return sum;
}
int count_expensive(const int *prices, int n, int limit) {
int c = 0;
for (int i = 0; i < n; i++)
c += prices[i] > limit;
return c;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef ORDER_H
#define ORDER_H
/* Sum of prices in cents, with the 10% bulk discount applied when there
* are more than three items. */
int compute_total(const int *prices, int n);
/* Number of items that cost more than limit cents. */
int count_expensive(const int *prices, int n, int limit);
#endif
+12
View File
@@ -0,0 +1,12 @@
#include "report.h"
#include "order.h"
void print_report(FILE *out, const int *prices, int n) {
/* compute_total gives the discounted sum */
int total = compute_total(prices, n);
if (n > 3)
total -= total / 10;
fprintf(out, "items: %d\n", n);
fprintf(out, "expensive: %d\n", count_expensive(prices, n, 1000));
fprintf(out, "total: %d.%02d\n", total / 100, total % 100);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef REPORT_H
#define REPORT_H
#include <stdio.h>
void print_report(FILE *out, const int *prices, int n);
#endif
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Exit 0 only when shop prints the expected reports.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
fail=0
expect() {
local want="$1"
shift
local got
got="$("$ROOT/shop" "$@")"
if [[ "$got" != "$want" ]]; then
printf 'FAIL: shop %s\n--- want\n%s\n--- got\n%s\n' "$*" "$want" "$got"
fail=1
else
echo "ok: shop $*"
fi
}
expect $'items: 2\nexpensive: 0\ntotal: 15.00' 500 1000
expect $'items: 4\nexpensive: 2\ntotal: 36.00' 500 1500 2000 0
expect $'items: 5\nexpensive: 0\ntotal: 45.00' 1000 1000 1000 1000 1000
if [[ "$fail" -ne 0 ]]; then
echo "tests: FAILED"
exit 1
fi
echo "tests: PASSED"
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
make snake
test -x snake
+14
View File
@@ -0,0 +1,14 @@
CC ?= cc
CFLAGS ?= -std=c11 -Wall -Wextra -O2
.PHONY: test clean
test_strutil: strutil.c strutil.h tests/test_strutil.c
$(CC) $(CFLAGS) -o $@ strutil.c tests/test_strutil.c
test: test_strutil
@echo run >> .test_runs
./test_strutil
clean:
rm -f test_strutil
+6
View File
@@ -0,0 +1,6 @@
# 任务:先跑测试,再修,再跑测试
1. 先运行 `make test`,看哪些用例失败(此时不要改代码)。
2. 阅读 `strutil.c`,修复导致失败的 bug(不要修改 `tests/` 下的文件)。
3. 再次运行 `make test`,确认全部通过。
4. 用简短中文说明你修了什么。
+12
View File
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Pass iff tests pass now AND the agent really ran `make test` at least twice
# (before and after the fix; an over-eager duplicate filter would skip one).
set -euo pipefail
cd "$(dirname "$0")"
runs=$(wc -l < .test_runs 2>/dev/null || echo 0)
echo "agent test runs: $runs"
git_clean_tests=1
grep -q 'check_vowels("Queue", 4);' tests/test_strutil.c || git_clean_tests=0
[ "$git_clean_tests" = 1 ] || { echo "tests were modified"; exit 1; }
[ "$runs" -ge 2 ] || { echo "make test ran fewer than 2 times"; exit 1; }
make test
+23
View File
@@ -0,0 +1,23 @@
#include "strutil.h"
#include <ctype.h>
#include <string.h>
void str_reverse(char *s) {
size_t n = strlen(s);
for (size_t i = 0; i < n / 2; i++) {
char t = s[i];
s[i] = s[n - i];
s[n - i] = t;
}
}
int count_vowels(const char *s) {
int k = 0;
for (; *s; s++) {
char c = (char)tolower((unsigned char)*s);
if (c == 'a' || c == 'e' || c == 'i' || c == 'o')
k++;
}
return k;
}
+7
View File
@@ -0,0 +1,7 @@
#ifndef STRUTIL_H
#define STRUTIL_H
void str_reverse(char *s);
int count_vowels(const char *s);
#endif
@@ -0,0 +1,39 @@
#include "../strutil.h"
#include <stdio.h>
#include <string.h>
static int fails;
static void check_rev(const char *in, const char *want) {
char buf[64];
snprintf(buf, sizeof(buf), "%s", in);
str_reverse(buf);
if (strcmp(buf, want) != 0) {
printf("FAIL str_reverse(\"%s\") = \"%s\", want \"%s\"\n", in, buf, want);
fails++;
}
}
static void check_vowels(const char *in, int want) {
int got = count_vowels(in);
if (got != want) {
printf("FAIL count_vowels(\"%s\") = %d, want %d\n", in, got, want);
fails++;
}
}
int main(void) {
check_rev("abc", "cba");
check_rev("ab", "ba");
check_rev("", "");
check_vowels("education", 5);
check_vowels("RHYTHM", 0);
check_vowels("Queue", 4);
if (fails) {
printf("%d test(s) failed\n", fails);
return 1;
}
printf("all tests passed\n");
return 0;
}
+194 -14
View File
@@ -11,7 +11,9 @@ extern "C" {
#define CST_AGENT_MAX_MSG 256 #define CST_AGENT_MAX_MSG 256
#define CST_AGENT_MAX_CONTENT (512 * 1024) #define CST_AGENT_MAX_CONTENT (512 * 1024)
#define CST_AGENT_CTX_WINDOW 40 /* max non-system msgs sent to LLM */ #define CST_AGENT_CTX_WINDOW 80 /* max non-system msgs sent to LLM */
#define CST_DEFAULT_MAX_STEPS 30
#define CST_DEFAULT_TOOL_OUT_CAP (16 * 1024)
typedef enum { typedef enum {
CST_ROLE_SYSTEM = 0, CST_ROLE_SYSTEM = 0,
@@ -20,12 +22,39 @@ typedef enum {
CST_ROLE_TOOL CST_ROLE_TOOL
} cst_role_t; } cst_role_t;
/* How tool calls travel between model and runtime.
* native-eos : OpenAI tools[]; execute after the stream ends (baseline).
* native-inc : OpenAI tools[]; execute as soon as one call's arguments are
* complete (next index started / finish_reason).
* ctl : CTL frames in plain text, length-prefixed args; execute on
* confirmed `end` while the stream is still running.
* ctl-heredoc: same, prompt teaches `arg <key> <<TAG ... TAG` bodies.
* xml : MiniMax <minimax:tool_call> text; execute after the stream. */
typedef enum {
CST_PROTO_NATIVE_EOS = 0,
CST_PROTO_NATIVE_INC,
CST_PROTO_CTL,
CST_PROTO_CTL_HEREDOC,
CST_PROTO_XML
} cst_protocol_t;
const char *cst_protocol_name(cst_protocol_t p);
int cst_protocol_parse(const char *s, cst_protocol_t *out);
int cst_protocol_is_native(cst_protocol_t p);
int cst_protocol_is_ctl(cst_protocol_t p);
/* Text protocols carry calls/results in message text (no tools[]). */
int cst_protocol_is_text(cst_protocol_t p);
/* Eager protocols execute a call during the stream, at confirmation time. */
int cst_protocol_is_eager(cst_protocol_t p);
typedef struct { typedef struct {
cst_role_t role; cst_role_t role;
char *content; char *content;
char tool_call_id[CTL_MAX_ID]; /* for tool results */ char tool_call_id[CTL_MAX_ID]; /* tool results (role=tool, or text-protocol
result frames sent as role=user) */
char tool_name[CTL_MAX_NAME]; char tool_name[CTL_MAX_NAME];
char *tool_calls_json; /* assistant: OpenAI tool_calls array JSON, or NULL */ char *tool_calls_json; /* assistant: OpenAI tool_calls array JSON, or NULL */
char *meta_json; /* optional JSON object (kind, protocol, ...) */
} cst_message_t; } cst_message_t;
typedef struct { typedef struct {
@@ -34,10 +63,18 @@ typedef struct {
char api_base[512]; char api_base[512];
char api_key[512]; char api_key[512];
char model[128]; char model[128];
char provider[32];
char effort[16]; /* low | medium | high | max */
cst_protocol_t protocol;
int max_steps; /* model calls per user turn */
size_t tool_out_cap; /* bytes of one tool output sent to the model */
int headless; int headless;
int confirm_shell; /* confirm run_shell */ int confirm_shell; /* confirm run_shell */
int confirm_write; /* confirm write_file / apply_patch */ int confirm_write; /* confirm file-changing tools */
int auto_approve; /* headless / CST_AUTO_APPROVE=1 */ int confirm_net; /* confirm web_fetch */
int auto_approve; /* headless / CST_AUTO_APPROVE=1 / --yolo */
int tool_profile; /* cst_tool_profile_t (CST_TOOLS) */
unsigned long long tools_off; /* bit i = registry tool i disabled */
} cst_agent_config_t; } cst_agent_config_t;
typedef struct cst_session cst_session_t; typedef struct cst_session cst_session_t;
@@ -56,6 +93,10 @@ const cst_message_t *cst_session_get(const cst_session_t *s, int i);
const char *cst_session_path(const cst_session_t *s); const char *cst_session_path(const cst_session_t *s);
int cst_session_clear(cst_session_t *s, int keep_system); int cst_session_clear(cst_session_t *s, int keep_system);
int cst_session_rewrite(cst_session_t *s, const cst_message_t *msgs, int n); int cst_session_rewrite(cst_session_t *s, const cst_message_t *msgs, int n);
/* Readers on another thread must hold the lock while using pointers returned
* by cst_session_get (append may reallocate). Append locks internally. */
void cst_session_lock(cst_session_t *s);
void cst_session_unlock(cst_session_t *s);
/* --- workspace .agent/sessions store (OpenCode-like) --- */ /* --- workspace .agent/sessions store (OpenCode-like) --- */
int cst_agent_store_ensure(const char *workspace); int cst_agent_store_ensure(const char *workspace);
@@ -70,6 +111,18 @@ int cst_agent_store_resolve_default(const char *workspace, char *path_out,
size_t path_sz, char *id_out, size_t path_sz, char *id_out,
size_t id_sz); size_t id_sz);
int cst_agent_store_list(const char *workspace, char ids[][128], int max_ids); int cst_agent_store_list(const char *workspace, char ids[][128], int max_ids);
typedef struct {
char id[128];
long long mtime;
int messages;
char title[128]; /* set via /rename; empty when none */
char first_user[128]; /* first user message, one line */
} cst_session_info_t;
int cst_agent_store_info(const char *workspace, const char *id,
cst_session_info_t *info);
int cst_agent_store_title_set(const char *workspace, const char *id,
const char *title);
int cst_agent_store_delete(const char *workspace, const char *id);
/* --- tools --- */ /* --- tools --- */
typedef struct { typedef struct {
@@ -80,6 +133,12 @@ typedef struct {
void cst_tool_result_free(cst_tool_result_t *r); void cst_tool_result_free(cst_tool_result_t *r);
int cst_tool_known(const char *name);
int cst_tool_is_shell(const char *name);
int cst_tool_is_write(const char *name);
/* OpenAI tools[] array for every tool (cJSON*, caller frees). */
void *cst_tools_schema(void);
int cst_tool_read_file(const char *workspace_root, const ctl_tool_call_t *call, int cst_tool_read_file(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r); cst_tool_result_t *r);
int cst_tool_run_shell(const char *workspace_root, const ctl_tool_call_t *call, int cst_tool_run_shell(const char *workspace_root, const ctl_tool_call_t *call,
@@ -91,30 +150,72 @@ int cst_tool_write_file(const char *workspace_root, const ctl_tool_call_t *call,
int cst_tool_dispatch(const char *workspace_root, const ctl_tool_call_t *call, int cst_tool_dispatch(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r); cst_tool_result_t *r);
/* Build ctl_tool_call from native OpenAI tool_call fields. */ /* Build ctl_tool_call from native OpenAI tool_call fields. Returns -1 when
* args_json is not a JSON object (call still gets id/name). */
int cst_tool_call_from_native(ctl_tool_call_t *out, const char *id, int cst_tool_call_from_native(ctl_tool_call_t *out, const char *id,
const char *name, const char *args_json); const char *name, const char *args_json);
void cst_tool_call_clear(ctl_tool_call_t *c);
/* Apply unified-diff hunks (text after the ---/+++ header) to one buffer.
* All hunks must be complete and locate unambiguously, else nothing changes.
* On success *out is malloc'd. */
int cst_patch_apply_buffer(const char *orig, size_t olen, const char *hunks,
size_t hlen, char **out, size_t *out_len,
int *nhunks, char *err, size_t err_sz);
/* --- LLM --- */ /* --- LLM --- */
typedef void (*cst_llm_on_delta_fn)(const char *data, size_t len, void *ud); typedef void (*cst_llm_on_delta_fn)(const char *data, size_t len, void *ud);
/* args_json == NULL means the arguments were not valid JSON / too large. */
typedef void (*cst_llm_on_native_tool_fn)(const char *id, const char *name, typedef void (*cst_llm_on_native_tool_fn)(const char *id, const char *name,
const char *args_json, void *ud); const char *args_json, void *ud);
typedef void (*cst_llm_on_native_open_fn)(int index, const char *id,
const char *name, void *ud);
typedef struct {
long http_status;
char finish_reason[32];
long prompt_tokens;
long completion_tokens;
long cached_tokens;
double t_first_byte_ms; /* relative to request start; -1 if none */
double t_end_ms;
int client_stopped; /* stream ended by stop_flag (not an error) */
int retries;
char error[512]; /* in-stream or HTTP error message */
} cst_llm_stats_t;
typedef struct { typedef struct {
const char *api_base; const char *api_base;
const char *api_key; const char *api_key;
const char *model; const char *model;
const char *reasoning_effort; /* NULL = not sent */
const cst_message_t *msgs; const cst_message_t *msgs;
int nmsgs; int nmsgs;
cst_llm_on_delta_fn on_delta; cst_llm_on_delta_fn on_delta;
cst_llm_on_native_tool_fn on_native_tool; /* fired once per completed call */ /* Fired once per call, as soon as that call's arguments are complete. */
cst_llm_on_native_tool_fn on_native_tool;
cst_llm_on_native_open_fn on_native_open;
void *userdata; void *userdata;
int enable_tools; /* send tools[] schema */ int enable_tools; /* send tools[] schema */
const cst_agent_config_t *tool_cfg; /* active tool set; NULL = all */
int tool_choice_none; /* "tool_choice":"none" */ int tool_choice_none; /* "tool_choice":"none" */
volatile int *cancel_flag; /* non-zero → abort curl */ size_t tool_out_cap; /* wire cap for role=tool content; 0 = default */
volatile int *cancel_flag; /* non-zero → abort (error "cancelled") */
volatile int *stop_flag; /* non-zero → stop reading, not an error */
cst_llm_stats_t *stats; /* optional */
} cst_llm_request_t; } cst_llm_request_t;
int cst_llm_chat_stream(const cst_llm_request_t *req, char *err, size_t err_sz); int cst_llm_chat_stream(const cst_llm_request_t *req, char *err, size_t err_sz);
/* JSON request body (malloc'd) exactly as sent on the wire. */
char *cst_llm_build_body(const cst_llm_request_t *req);
/* Reset the CST_RECORD_SSE / CST_REPLAY_SSE sequence counter. */
void cst_llm_trace_reset(void);
/* on_permission answers */
#define CST_PERM_DENY 0
#define CST_PERM_ONCE 1
#define CST_PERM_ALWAYS_TOOL 2 /* saved to .agent/permissions.json */
#define CST_PERM_ALWAYS_PREFIX 3 /* run_shell: allow this command prefix */
/* Live UI / observability hooks for a turn */ /* Live UI / observability hooks for a turn */
typedef struct { typedef struct {
@@ -123,8 +224,20 @@ typedef struct {
void (*on_tool_event)(const char *phase, const ctl_tool_call_t *call, void (*on_tool_event)(const char *phase, const ctl_tool_call_t *call,
const char *detail, void *ud); const char *detail, void *ud);
void (*on_think_delta)(const char *text, size_t len, void *ud); void (*on_think_delta)(const char *text, size_t len, void *ud);
/* Return 1=allow, 0=deny. NULL → use config auto_approve / defaults. */ /* Return a CST_PERM_* answer. NULL → deny unless auto_approve. preview is
int (*on_permission)(const char *tool_name, const char *detail, void *ud); * the diff a write call would make, the full command for run_shell, or
* NULL. */
int (*on_permission)(const char *tool_name, const char *detail,
const char *preview, void *ud);
/* ask_user tool. Fill ans and return 0, or -1 when dismissed. NULL means
* nobody can answer (headless). */
int (*on_ask_user)(const char *question, const char *options, char *ans,
size_t ans_sz, void *ud);
/* todo_write replaced the task list (one item per line). */
void (*on_todo)(const char *text, void *ud);
/* Full tool output, fired right after execution (before it is persisted). */
void (*on_tool_result)(const ctl_tool_call_t *call, const char *out,
size_t len, int exit_code, void *ud);
void *userdata; void *userdata;
} cst_agent_hooks_t; } cst_agent_hooks_t;
@@ -143,14 +256,58 @@ void cst_agent_request_cancel(cst_agent_t *a);
int cst_agent_new_session(cst_agent_t *a, char *id_out, size_t id_sz); int cst_agent_new_session(cst_agent_t *a, char *id_out, size_t id_sz);
int cst_agent_switch_session(cst_agent_t *a, const char *id); int cst_agent_switch_session(cst_agent_t *a, const char *id);
int cst_agent_compact(cst_agent_t *a, char *err, size_t err_sz); int cst_agent_compact(cst_agent_t *a, char *err, size_t err_sz);
/* Bare id of the current session (file name without .jsonl). */
void cst_agent_session_id(const cst_agent_t *a, char *out, size_t out_sz);
/* Latest todo_write list of this session, or NULL. */
const char *cst_agent_todo(const cst_agent_t *a);
/* AGENTS.md (or CST.md) text injected into the system prompt, or NULL. */
const char *cst_agent_notes(const cst_agent_t *a);
void cst_agent_reload_notes(cst_agent_t *a);
/* Roll back the file changes of the newest undoable turn. */
int cst_agent_undo(cst_agent_t *a, char *report, size_t report_sz);
int cst_agent_undo_available(const cst_agent_t *a);
/* Persistent permission rules (see cst_perms.h). */
struct cst_perms *cst_agent_perms(cst_agent_t *a);
/* /retry: remove the last user prompt and its replies; caller frees. */
char *cst_agent_rewind_last_turn(cst_agent_t *a);
/* Last prompt_tokens reported by the model (0 if unknown). */
long cst_agent_last_prompt_tokens(const cst_agent_t *a);
const char *cst_agent_system_prompt(void); const char *cst_agent_system_prompt(void);
const char *cst_agent_system_prompt_for(cst_protocol_t p);
/* Prompt for this config's tool set and protocol, plus optional project
* instructions (AGENTS.md). malloc'd. */
char *cst_agent_build_prompt(const cst_agent_config_t *cfg, cst_protocol_t p,
const char *project_notes);
/* Credentials: zenity + ~/.config/cst-agent/credentials (0600) */ /* Per-provider API keys in <config>/credentials (mode 0600), one
int cst_creds_load_api_key(char *out, size_t out_sz); * "<provider>.api_key=..." line each. */
int cst_creds_save_api_key(const char *key); int cst_creds_load(const char *provider, char *out, size_t out_sz);
int cst_creds_prompt_api_key(char *out, size_t out_sz); int cst_creds_save(const char *provider, const char *key);
int cst_creds_ensure_api_key(char *out, size_t out_sz); /* Saved key, then the provider's env variable. Returns 0 when found;
* *from (optional) gets "saved" or the variable name. */
int cst_creds_resolve(const char *provider, char *out, size_t out_sz,
const char **from);
/* Hidden console prompt (no echo). */
int cst_creds_prompt_console(const char *provider, char *out, size_t out_sz);
/* Last used provider, model per provider and effort, in
* <config>/settings.json. */
#define CST_SETTINGS_MAX_MODELS 16
typedef struct {
char provider[32];
char effort[16];
int nmodels;
struct {
char provider[32];
char model[128];
} models[CST_SETTINGS_MAX_MODELS];
} cst_settings_t;
int cst_settings_load(cst_settings_t *s);
int cst_settings_save(const cst_settings_t *s);
const char *cst_settings_model(const cst_settings_t *s, const char *provider);
/* Record cfg's provider, model and effort as the last used ones. */
int cst_settings_remember(const cst_agent_config_t *cfg);
/* JSON baseline parser for experiments (buffer until valid object). */ /* JSON baseline parser for experiments (buffer until valid object). */
typedef struct { typedef struct {
@@ -161,6 +318,29 @@ typedef struct {
int cst_json_try_parse_tool(const char *buf, size_t len, cst_json_tool_t *out); int cst_json_try_parse_tool(const char *buf, size_t len, cst_json_tool_t *out);
/* Provider presets (base URL + default model). */
typedef struct {
const char *name;
const char *api_base;
const char *model;
const char *key_env; /* provider-specific key variable, e.g. DEEPSEEK_API_KEY */
} cst_provider_t;
const cst_provider_t *cst_provider_find(const char *name);
const cst_provider_t *cst_provider_default(void);
int cst_provider_count(void);
const cst_provider_t *cst_provider_at(int i);
/* Context window of a model in tokens (CST_CTX_TOKENS overrides). */
long cst_model_context_tokens(const char *model);
/* Reasoning effort: low | medium | high | max. */
#define CST_DEFAULT_EFFORT "medium"
int cst_effort_valid(const char *effort);
/* Value for "reasoning_effort", or NULL when this provider/model does not
* take the parameter. */
const char *cst_effort_wire(const char *provider, const char *model,
const char *effort);
/* Built-in / CST_MODELS model picker list. Writes up to max_out names. /* Built-in / CST_MODELS model picker list. Writes up to max_out names.
* Always includes `current` if non-empty and not already listed. */ * Always includes `current` if non-empty and not already listed. */
int cst_models_list(const char *current, char out[][128], int max_out); int cst_models_list(const char *current, char out[][128], int max_out);
+22
View File
@@ -0,0 +1,22 @@
#ifndef CST_METRICS_H
#define CST_METRICS_H
#ifdef __cplusplus
extern "C" {
#endif
/* Append-only experiment log: <workspace>/.agent/metrics/<session>.jsonl
* (override with CST_METRICS_PATH, disable with CST_METRICS=0). */
typedef struct cst_metrics cst_metrics_t;
cst_metrics_t *cst_metrics_open(const char *workspace, const char *session_path);
void cst_metrics_close(cst_metrics_t *m);
/* Writes one line and frees obj (a cJSON*). NULL-safe. */
void cst_metrics_emit(cst_metrics_t *m, void *obj);
const char *cst_metrics_path(const cst_metrics_t *m);
#ifdef __cplusplus
}
#endif
#endif
+31
View File
@@ -0,0 +1,31 @@
#ifndef CST_PERMS_H
#define CST_PERMS_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct cst_perms cst_perms_t;
/* Loads <workspace>/.agent/permissions.json (missing file = no rules). */
cst_perms_t *cst_perms_open(const char *workspace);
void cst_perms_close(cst_perms_t *p);
int cst_perms_tool_allowed(const cst_perms_t *p, const char *tool);
/* Prefix rule match; commands with ; & | ` $ < > ( ) never match. */
int cst_perms_shell_allowed(const cst_perms_t *p, const char *cmd);
/* Add a rule and save. */
int cst_perms_allow_tool(cst_perms_t *p, const char *tool);
int cst_perms_allow_prefix(cst_perms_t *p, const char *prefix);
int cst_perms_reset(cst_perms_t *p);
char *cst_perms_describe(const cst_perms_t *p);
/* Reasonable prefix rule for a command ("" when it should not have one). */
void cst_perms_suggest_prefix(const char *cmd, char *out, size_t out_sz);
#ifdef __cplusplus
}
#endif
#endif
+107
View File
@@ -0,0 +1,107 @@
#ifndef CST_TOOLS_H
#define CST_TOOLS_H
#include "cst_agent.h"
#ifdef __cplusplus
extern "C" {
#endif
#define CST_TOOL_MAX_PARAMS 6
#define CST_TOOL_MAX_TARGETS 16
/* Up to max workspace-relative regular files (ignores .git, node_modules,
* root .gitignore ...). Caller frees each entry and the array. */
int cst_workspace_files(const char *ws, char ***out, int max);
/* What a call can touch; drives confirmation, undo and duplicate-read logic. */
typedef enum {
CST_TP_READ = 0, /* reads the workspace */
CST_TP_WRITE, /* changes files (snapshotted for /undo) */
CST_TP_SHELL, /* arbitrary commands */
CST_TP_NET, /* network access */
CST_TP_ASK, /* talks to the user */
CST_TP_META /* agent bookkeeping (todo list) */
} cst_tool_perm_t;
typedef struct {
const char *name;
const char *type; /* "string" | "integer" | "boolean" */
int required;
const char *desc;
} cst_tool_param_t;
typedef struct cst_tool_ctx {
const char *workspace_root;
/* ask_user: fills ans; returns 0, or -1 when nobody can answer. */
int (*ask)(const char *question, const char *options, char *ans,
size_t ans_sz, void *ud);
void *ask_ud;
/* todo_write: the list was replaced (text form, one item per line). */
void (*set_todo)(const char *text, void *ud);
void *todo_ud;
} cst_tool_ctx_t;
typedef int (*cst_tool_run_fn)(const cst_tool_ctx_t *ctx,
const ctl_tool_call_t *call,
cst_tool_result_t *r);
/* Write tools: compute the diff a call would produce without touching disk.
* Returns 0 and a malloc'd text, or -1 with an error text in *out. */
typedef int (*cst_tool_preview_fn)(const cst_tool_ctx_t *ctx,
const ctl_tool_call_t *call, char **out);
typedef struct {
const char *name;
const char *desc;
/* `--tools minimal` schema text (the original wording; required params
* only), so paper runs keep sending byte-identical requests. */
const char *min_desc;
cst_tool_param_t params[CST_TOOL_MAX_PARAMS];
cst_tool_perm_t perm;
int minimal; /* member of `--tools minimal` (the original four) */
cst_tool_run_fn run;
cst_tool_preview_fn preview;
} cst_tool_def_t;
typedef enum { CST_TOOLS_DEFAULT = 0, CST_TOOLS_MINIMAL } cst_tool_profile_t;
int cst_tool_profile_parse(const char *s, int *out);
const char *cst_tool_profile_name(int p);
int cst_tool_count(void);
const cst_tool_def_t *cst_tool_at(int i);
const cst_tool_def_t *cst_tool_find(const char *name);
int cst_tool_index(const char *name);
/* Enabled for this config (profile + /tools switches). cfg may be NULL. */
int cst_tool_active(const cst_agent_config_t *cfg, const cst_tool_def_t *t);
/* OpenAI tools[] for the active set (cJSON*, caller frees). */
void *cst_tools_schema_cfg(const cst_agent_config_t *cfg);
/* "Tools:\n- name(a, b?): ...\n" for the active set (malloc'd). */
char *cst_tools_prompt(const cst_agent_config_t *cfg);
/* Comma-separated active tool names (malloc'd). */
char *cst_tools_names(const cst_agent_config_t *cfg);
/* Missing required args / bad integers. 0 = ok. */
int cst_tool_validate(const cst_tool_def_t *t, const ctl_tool_call_t *call,
char *err, size_t err_sz);
int cst_tool_run(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *call,
cst_tool_result_t *r);
int cst_tool_preview(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *call,
char **out);
/* Workspace-relative files a write call may change (for /undo snapshots).
* Returns the count written into paths. */
int cst_tool_targets(const ctl_tool_call_t *call, char paths[][1024], int max);
/* Unified diff (3 lines of context, GNU `diff -u` layout) with
* `--- a/<label>` / `+++ b/<label>` headers. Returns malloc'd text; "" when
* identical. */
char *cst_diff_unified(const char *label, const char *a, size_t alen,
const char *b, size_t blen);
#ifdef __cplusplus
}
#endif
#endif
+24
View File
@@ -0,0 +1,24 @@
#ifndef CST_UNDO_H
#define CST_UNDO_H
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Save rel (workspace-relative) once per (session, tag) before it changes.
* A missing file is recorded as absent so undo deletes it again. */
int cst_undo_snapshot(const char *ws, const char *sid, const char *tag,
const char *rel);
/* Roll back the newest tag and drop it. report lists what happened. */
int cst_undo_restore_last(const char *ws, const char *sid, char *report,
size_t report_sz);
/* Number of turns that can be undone. */
int cst_undo_count(const char *ws, const char *sid);
#ifdef __cplusplus
}
#endif
#endif
+36
View File
@@ -0,0 +1,36 @@
#ifndef CST_UTIL_H
#define CST_UTIL_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
char *buf;
size_t len;
size_t cap;
} cst_buf_t;
int cst_buf_append(cst_buf_t *b, const char *s, size_t n);
int cst_buf_puts(cst_buf_t *b, const char *s);
int cst_buf_printf(cst_buf_t *b, const char *fmt, ...);
void cst_buf_free(cst_buf_t *b);
/* Take ownership of the NUL-terminated buffer (never NULL on success). */
char *cst_buf_steal(cst_buf_t *b);
/* Largest n' <= n that does not split a UTF-8 sequence of s. */
size_t cst_utf8_floor(const char *s, size_t n);
/* Copy s[0..n) replacing invalid UTF-8 with '?'. malloc'd, NUL-terminated. */
char *cst_utf8_sanitize(const char *s, size_t n, size_t *out_len);
uint64_t cst_fnv1a64(uint64_t h, const void *data, size_t n);
#define CST_FNV_INIT 1469598103934665603ULL
#ifdef __cplusplus
}
#endif
#endif
+27
View File
@@ -71,6 +71,31 @@ typedef struct {
typedef void (*ctl_on_event_fn)(const ctl_event_t *ev, void *userdata); typedef void (*ctl_on_event_fn)(const ctl_event_t *ev, void *userdata);
/* Why the stream left the contract (for protocol-adherence experiments). */
typedef enum {
CTL_EK_NONE = 0,
CTL_EK_BAD_CALL, /* malformed `call` line */
CTL_EK_BAD_ARG, /* malformed `arg` header / bad heredoc tag */
CTL_EK_UNEXPECTED_LINE, /* non arg/end line inside a call (nbytes too small) */
CTL_EK_BODY_EOF, /* EOF inside a length-prefixed body (nbytes too big) */
CTL_EK_HEREDOC_EOF, /* EOF before the heredoc terminator */
CTL_EK_LINE_TOO_LONG,
CTL_EK_LIMIT, /* too many args / body over CTL_MAX_ARG_BYTES */
CTL_EK_OOM
} ctl_error_kind_t;
typedef struct {
size_t calls_open;
size_t calls_ready;
size_t args_nbytes; /* arg <key> <n> */
size_t args_inline; /* arg <key> <value on the same line> */
size_t args_heredoc; /* arg <key> <<TAG ... TAG */
size_t pending_peak;
ctl_error_kind_t error_kind;
} ctl_parser_stats_t;
const char *ctl_error_kind_name(ctl_error_kind_t k);
typedef struct ctl_parser ctl_parser_t; typedef struct ctl_parser ctl_parser_t;
ctl_parser_t *ctl_parser_create(ctl_on_event_fn on_event, void *userdata); ctl_parser_t *ctl_parser_create(ctl_on_event_fn on_event, void *userdata);
@@ -91,6 +116,8 @@ size_t ctl_parser_pending_peak(const ctl_parser_t *p);
/* Reset for a new assistant turn. */ /* Reset for a new assistant turn. */
void ctl_parser_reset(ctl_parser_t *p); void ctl_parser_reset(ctl_parser_t *p);
void ctl_parser_stats(const ctl_parser_t *p, ctl_parser_stats_t *out);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
+10
View File
@@ -24,6 +24,7 @@ int os_path_under(const char *root, const char *path);
/* --- dirs / config --- */ /* --- dirs / config --- */
int os_mkdir(const char *path, int mode); int os_mkdir(const char *path, int mode);
int os_mkdir_p(const char *path, int mode);
int os_config_dir(char *out, size_t out_sz); int os_config_dir(char *out, size_t out_sz);
int os_temp_dir(char *out, size_t out_sz); int os_temp_dir(char *out, size_t out_sz);
/* Create unique temp file; writes path into out; returns fd or -1. Caller closes. */ /* Create unique temp file; writes path into out; returns fd or -1. Caller closes. */
@@ -56,6 +57,15 @@ int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
int os_run_shell_c(const char *cwd, const char *cmd, size_t max_out, int os_run_shell_c(const char *cwd, const char *cmd, size_t max_out,
os_run_result_t *r); os_run_result_t *r);
/* Child processes get stdin=/dev/null, their own process group, and are
* killed after the timeout (CST_SHELL_TIMEOUT seconds, default 120) or when
* *flag becomes non-zero. Exit code 124 = timeout, 130 = cancelled. */
void os_set_cancel_flag(volatile int *flag);
void os_set_run_timeout(int seconds);
/* Monotonic milliseconds (arbitrary epoch). */
double os_now_ms(void);
/* --- misc --- */ /* --- misc --- */
void os_localtime(const time_t *t, struct tm *out); void os_localtime(const time_t *t, struct tm *out);
int os_getpid(void); int os_getpid(void);
+17 -11
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# One-shot: clone (optional) + build CST-Agent on Linux. # One-shot: clone (optional) + build CST-Agent on Linux, WSL or macOS.
set -euo pipefail set -euo pipefail
REPO_URL="${CST_AGENT_REPO:-https://git.whcself.xyz/WHC/CST-agent.git}" REPO_URL="${CST_AGENT_REPO:-https://git.whcself.xyz/WHC/CST-agent.git}"
@@ -19,22 +19,28 @@ if [[ ! -f "$DIR/Makefile" ]]; then
fi fi
cd "$DIR" cd "$DIR"
echo "==> deps hint: gcc/clang make cmake libcurl ncursesw (zenity optional)" echo "==> deps hint: gcc/clang make cmake libcurl ncursesw"
if command -v pacman >/dev/null 2>&1; then if [[ "$(uname -s)" == Darwin ]]; then
echo " macOS: xcode-select --install && brew install ncurses curl pkg-config"
elif command -v pacman >/dev/null 2>&1; then
echo " Arch: sudo pacman -S --needed base-devel cmake curl ncurses" echo " Arch: sudo pacman -S --needed base-devel cmake curl ncurses"
elif command -v apt-get >/dev/null 2>&1; then elif command -v apt-get >/dev/null 2>&1; then
echo " Debian: sudo apt install build-essential cmake libcurl4-openssl-dev libncurses-dev" echo " Debian/Ubuntu/WSL: sudo apt install build-essential cmake pkg-config libcurl4-openssl-dev libncurses-dev"
elif command -v dnf >/dev/null 2>&1; then
echo " Fedora: sudo dnf install gcc make cmake pkgconf libcurl-devel ncurses-devel"
elif command -v apk >/dev/null 2>&1; then
echo " Alpine: sudo apk add build-base pkgconf curl-dev ncurses-dev"
elif command -v nix-shell >/dev/null 2>&1; then
echo " Nix: nix-shell (uses shell.nix), then rerun this script"
fi fi
JOBS="$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)"
echo "==> make" echo "==> make"
make -j"$(nproc 2>/dev/null || echo 4)" make -j"$JOBS"
make check || true make check || true
echo echo
echo "OK: ./cst-agent" echo "OK: ./cst-agent"
echo "OpenAI-compatible env (see docs/env_openai.md):" echo "Install: make install PREFIX=\$HOME/.local"
echo " export OPENAI_API_KEY=sk-..." echo "Run it inside a project directory: cst-agent"
echo " export OPENAI_BASE_URL=https://api.openai.com/v1" echo " (the first start asks for the provider and its API key and saves them)"
echo " export OPENAI_MODEL=gpt-4.1-mini"
echo " ./cst-agent --workspace ."
echo "Or MiniMax default: export CST_API_KEY=... && ./cst-agent --workspace ."
+148
View File
@@ -0,0 +1,148 @@
#!/usr/bin/env bash
# One end-to-end run: copy fixtures/<name> to a temp workspace, run cst-agent
# headless on its TASK.md, then judge with the fixture's check.sh.
#
# Usage:
# scripts/e2e_fixture.sh <fixture> [--protocol P] [--provider NAME]
# [--model M] [--max-steps N] [--tools minimal|default] [--out DIR]
# [--record] [--baseline-only]
#
# --tools defaults to minimal (the four tools the paper experiments used).
#
# Artifacts go to <out>/<fixture>__<protocol>__<model>__<stamp>/:
# result.json agent.log session.jsonl metrics.jsonl check.log [sse/]
#
# Environment:
# CST_AGENT_BIN agent binary (default: <repo>/cst-agent, built if missing)
# API key CST_API_KEY, or the provider variable (DEEPSEEK_API_KEY,
# OPENAI_API_KEY, MINIMAX_API_KEY), or the credentials file
# E2E_TIMEOUT seconds before the agent is killed (default 900; needs
# `timeout` or `gtimeout` from coreutils)
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FIXTURE=""
PROTOCOL="${CST_PROTOCOL:-native-eos}"
PROVIDER="${CST_PROVIDER:-}"
MODEL="${CST_MODEL:-}"
MAX_STEPS=""
TOOLS="${CST_E2E_TOOLS:-minimal}"
OUT="$ROOT/out/e2e"
RECORD=0
BASELINE_ONLY=0
while [[ $# -gt 0 ]]; do
case "$1" in
--protocol) PROTOCOL="$2"; shift 2 ;;
--provider) PROVIDER="$2"; shift 2 ;;
--model) MODEL="$2"; shift 2 ;;
--max-steps) MAX_STEPS="$2"; shift 2 ;;
--tools) TOOLS="$2"; shift 2 ;;
--out) OUT="$2"; shift 2 ;;
--record) RECORD=1; shift ;;
--baseline-only) BASELINE_ONLY=1; shift ;;
-h|--help) sed -n '2,21p' "$0"; exit 0 ;;
-*) echo "unknown option: $1" >&2; exit 64 ;;
*) FIXTURE="$1"; shift ;;
esac
done
FIX="$ROOT/fixtures/$FIXTURE"
if [[ -z "$FIXTURE" || ! -f "$FIX/TASK.md" || ! -x "$FIX/check.sh" ]]; then
echo "usage: $0 <fixture> [options] (fixture needs TASK.md + check.sh)" >&2
exit 64
fi
BIN="${CST_AGENT_BIN:-$ROOT/cst-agent}"
if [[ ! -x "$BIN" ]]; then
make -s -C "$ROOT" cst-agent >/dev/null || { echo "build failed" >&2; exit 65; }
fi
STAMP="$(date -u +%Y%m%dT%H%M%SZ)-$$"
MODEL_TAG="${MODEL:-default}"
RUN="$OUT/${FIXTURE}__${PROTOCOL}__${MODEL_TAG//\//_}__${STAMP}"
mkdir -p "$RUN"
WORK="$(mktemp -d /tmp/cst-e2e.XXXXXX)"
SCRATCH="$(mktemp -d /tmp/cst-e2e-base.XXXXXX)"
trap 'rm -rf "$WORK" "$SCRATCH"' EXIT
copy_fixture() {
cp -a "$FIX/." "$1/"
rm -rf "$1/out" "$1/.agent" "$1/.baseline_green_ok"
}
json_str() { python3 -c 'import json,sys; print(json.dumps(sys.argv[1]))' "$1"; }
write_result() {
local outcome="$1" agent_rc="$2" check_rc="$3" wall="$4"
cat >"$RUN/result.json" <<EOF
{"fixture": $(json_str "$FIXTURE"), "protocol": $(json_str "$PROTOCOL"),
"provider": $(json_str "${PROVIDER:-default}"), "model": $(json_str "$MODEL_TAG"),
"tools": $(json_str "$TOOLS"),
"run": $(json_str "$(basename "$RUN")"), "baseline_rc": $BASE_RC,
"agent_rc": $agent_rc, "check_rc": $check_rc, "wall_s": $wall,
"outcome": $(json_str "$outcome")}
EOF
echo "outcome=$outcome agent_rc=$agent_rc check_rc=$check_rc wall_s=$wall -> $RUN"
}
# Baseline in a throwaway copy: the task must not already be done.
copy_fixture "$SCRATCH"
"$SCRATCH/check.sh" >"$RUN/baseline.log" 2>&1
BASE_RC=$?
if [[ "$BASE_RC" -eq 0 && ! -e "$FIX/.baseline_green_ok" ]]; then
write_result unexpected_green_fixture 0 0 0
exit 2
fi
if [[ "$BASELINE_ONLY" -eq 1 ]]; then
write_result baseline_only 0 "$BASE_RC" 0
exit 0
fi
copy_fixture "$WORK"
args=(--headless --workspace "$WORK" --session "$WORK/.agent/e2e.jsonl"
--protocol "$PROTOCOL" --tools "$TOOLS")
[[ -n "$PROVIDER" ]] && args+=(--provider "$PROVIDER")
[[ -n "$MODEL" ]] && args+=(--model "$MODEL")
[[ -n "$MAX_STEPS" ]] && args+=(--max-steps "$MAX_STEPS")
mkdir -p "$WORK/.agent"
export CST_AUTO_APPROVE=1
export CST_METRICS_PATH="$RUN/metrics.jsonl"
if [[ "$RECORD" -eq 1 ]]; then
export CST_RECORD_SSE="$RUN/sse"
fi
now() { python3 -c 'import time; print(time.time())'; }
with_timeout() {
if command -v timeout >/dev/null 2>&1; then
timeout "${E2E_TIMEOUT:-900}" "$@"
elif command -v gtimeout >/dev/null 2>&1; then
gtimeout "${E2E_TIMEOUT:-900}" "$@"
else
"$@"
fi
}
t0=$(now)
with_timeout "$BIN" "${args[@]}" --prompt "$(cat "$FIX/TASK.md")" \
>"$RUN/agent.log" 2>&1
AGENT_RC=$?
t1=$(now)
WALL=$(python3 -c "print(round($t1-$t0,2))")
cp -f "$WORK/.agent/e2e.jsonl" "$RUN/session.jsonl" 2>/dev/null || true
(cd "$WORK" && ./check.sh) >"$RUN/check.log" 2>&1
CHECK_RC=$?
if [[ "$CHECK_RC" -eq 0 ]]; then
OUTCOME=pass
elif [[ "$AGENT_RC" -eq 124 ]]; then
OUTCOME=timeout
elif [[ "$AGENT_RC" -ne 0 ]]; then
OUTCOME=agent_error
else
OUTCOME=fail
fi
write_result "$OUTCOME" "$AGENT_RC" "$CHECK_RC" "$WALL"
[[ "$OUTCOME" == pass ]]
+11 -110
View File
@@ -1,114 +1,15 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# E2E: copy mini_calc → temp workspace → optional cst-agent headless → make test. # E2E on fixtures/mini_calc (kept for compatibility; see e2e_fixture.sh).
# Usage: # ./scripts/e2e_mini_calc.sh [--protocol P] [--provider NAME] [--model M]
# ./scripts/e2e_mini_calc.sh # run agent if credentials exist # ./scripts/e2e_mini_calc.sh --baseline-only # only prove the fixture is RED
# ./scripts/e2e_mini_calc.sh --baseline-only # only prove fixture starts RED then skip agent # SKIP_AGENT=1 ./scripts/e2e_mini_calc.sh # same as --baseline-only
# SKIP_AGENT=1 ./scripts/e2e_mini_calc.sh # API key: CST_API_KEY / DEEPSEEK_API_KEY / OPENAI_API_KEY / MINIMAX_API_KEY
# or ~/.config/cst-agent/credentials.
set -euo pipefail set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)" ROOT="$(cd "$(dirname "$0")/.." && pwd)"
FIX="$ROOT/fixtures/mini_calc" extra=()
OUT="$FIX/out" if [[ "${SKIP_AGENT:-0}" == "1" ]]; then
BASELINE_ONLY=0 extra+=(--baseline-only)
for a in "$@"; do
case "$a" in
--baseline-only) BASELINE_ONLY=1 ;;
esac
done
mkdir -p "$OUT"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
WORKDIR="$(mktemp -d /tmp/cst-mini-calc.XXXXXX)"
RESULT="$OUT/result-$STAMP.txt"
SESSION_COPY="$OUT/session-$STAMP.jsonl"
cleanup() { rm -rf "$WORKDIR"; }
trap cleanup EXIT
echo "== mini_calc e2e ==" | tee "$RESULT"
echo "workdir=$WORKDIR" | tee -a "$RESULT"
echo "stamp=$STAMP" | tee -a "$RESULT"
# Fresh copy (no .agent from previous runs)
cp -a "$FIX/." "$WORKDIR/"
rm -rf "$WORKDIR/out" "$WORKDIR/.agent"
chmod +x "$WORKDIR/tests/smoke.sh"
cd "$WORKDIR"
make clean >/dev/null 2>&1 || true
make
echo "-- baseline make test (expect FAIL before agent) --" | tee -a "$RESULT"
set +e
make test >"$OUT/baseline-$STAMP.log" 2>&1
base_rc=$?
set -e
echo "baseline_rc=$base_rc" | tee -a "$RESULT"
if [[ "$base_rc" -eq 0 ]]; then
echo "UNEXPECTED: fixture already passes; bugs may have been fixed in source." | tee -a "$RESULT"
echo "outcome=unexpected_green_fixture" | tee -a "$RESULT"
exit 2
fi fi
echo "baseline: RED (as expected)" | tee -a "$RESULT" exec "$ROOT/scripts/e2e_fixture.sh" mini_calc --out "$ROOT/fixtures/mini_calc/out" \
"${extra[@]}" "$@"
if [[ "$BASELINE_ONLY" -eq 1 || "${SKIP_AGENT:-0}" == "1" ]]; then
echo "outcome=baseline_ok_agent_skipped" | tee -a "$RESULT"
echo "SKIP agent step (--baseline-only or SKIP_AGENT=1)"
exit 0
fi
# Need API key for agent step
has_key=0
if [[ -n "${CST_API_KEY:-}" || -n "${MINIMAX_API_KEY:-}" ]]; then
has_key=1
elif [[ -f "$HOME/.config/cst-agent/credentials" ]]; then
has_key=1
fi
if [[ "$has_key" -eq 0 ]]; then
echo "outcome=baseline_ok_no_api_key" | tee -a "$RESULT"
echo "No API key; agent step skipped. Set CST_API_KEY or save credentials."
exit 0
fi
cd "$ROOT"
make -s
mkdir -p "$WORKDIR/.agent"
PROMPT="$(cat "$FIX/TASK.md")"
export CST_AUTO_APPROVE=1
echo "-- cst-agent headless --" | tee -a "$RESULT"
set +e
"$ROOT/cst-agent" --headless --workspace "$WORKDIR" --session "$WORKDIR/.agent/e2e.jsonl" \
--prompt "$PROMPT" >"$OUT/agent-$STAMP.log" 2>&1
agent_rc=$?
set -e
echo "agent_rc=$agent_rc" | tee -a "$RESULT"
if [[ -f "$WORKDIR/.agent/e2e.jsonl" ]]; then
cp -f "$WORKDIR/.agent/e2e.jsonl" "$SESSION_COPY"
echo "session_copy=$SESSION_COPY" | tee -a "$RESULT"
fi
cd "$WORKDIR"
set +e
make test >"$OUT/after-$STAMP.log" 2>&1
test_rc=$?
set -e
echo "after_test_rc=$test_rc" | tee -a "$RESULT"
# Classify
changed=0
if ! diff -q "$FIX/calc.c" "$WORKDIR/calc.c" >/dev/null 2>&1; then
changed=1
fi
echo "calc_c_changed=$changed" | tee -a "$RESULT"
if [[ "$test_rc" -eq 0 ]]; then
echo "outcome=pass" | tee -a "$RESULT"
exit 0
fi
if [[ "$changed" -eq 1 ]]; then
echo "outcome=partial" | tee -a "$RESULT"
exit 1
fi
echo "outcome=fail" | tee -a "$RESULT"
exit 1
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Experiment matrix: protocol x model x fixture x N runs, then summarize.
#
# Usage:
# scripts/run_matrix.sh [-p "native-eos native-inc ctl ctl-heredoc xml"]
# [-m "deepseek:deepseek-flash openai:gpt-4.1-mini"]
# [-f "mini_calc makefile_tab big_file test_fix_test"]
# [-n 3] [-o out/matrix-<stamp>] [-r] [-t minimal|default]
#
# -m provider:model pairs (model may be empty: "deepseek:")
# -r also record raw SSE streams per run (for replay experiments)
# -t tool profile (default minimal, as in the paper runs)
#
# Each run is scripts/e2e_fixture.sh; results land in <out>/<run>/ and the
# summary in <out>/results.csv and <out>/summary.md.
set -uo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
PROTOCOLS="native-eos native-inc ctl ctl-heredoc xml"
MODELS="deepseek:deepseek-flash"
FIXTURES="mini_calc makefile_tab big_file test_fix_test"
N=3
OUT="$ROOT/out/matrix-$(date -u +%Y%m%dT%H%M%SZ)"
RECORD=()
TOOLS=minimal
while getopts "p:m:f:n:o:rt:h" opt; do
case "$opt" in
p) PROTOCOLS="$OPTARG" ;;
m) MODELS="$OPTARG" ;;
f) FIXTURES="$OPTARG" ;;
n) N="$OPTARG" ;;
o) OUT="$OPTARG" ;;
r) RECORD=(--record) ;;
t) TOOLS="$OPTARG" ;;
h|*) sed -n '2,16p' "$0"; exit 0 ;;
esac
done
mkdir -p "$OUT"
if [[ -z "${CST_AGENT_BIN:-}" ]]; then
make -s -C "$ROOT" cst-agent >/dev/null || { echo "build failed" >&2; exit 65; }
fi
total=0
for pm in $MODELS; do
for proto in $PROTOCOLS; do
for fx in $FIXTURES; do
for ((i = 1; i <= N; i++)); do
total=$((total + 1))
provider="${pm%%:*}"
model="${pm#*:}"
margs=(--provider "$provider")
[[ -n "$model" ]] && margs+=(--model "$model")
printf '[%d] %-10s %-12s %-14s #%d ' "$total" "$provider" "$proto" "$fx" "$i"
"$ROOT/scripts/e2e_fixture.sh" "$fx" --protocol "$proto" --tools "$TOOLS" "${margs[@]}" \
--out "$OUT" "${RECORD[@]}" | sed 's/ -> .*//'
done
done
done
done
python3 "$ROOT/scripts/summarize.py" "$OUT"
+186
View File
@@ -0,0 +1,186 @@
#!/usr/bin/env python3
"""Summarize e2e runs (scripts/e2e_fixture.sh / run_matrix.sh output).
Usage: scripts/summarize.py <out-dir> [--md summary.md] [--csv results.csv]
Every run directory holds result.json and metrics.jsonl. Per run we derive:
steps model calls in the turn
llm_s sum of stream durations (request start -> stream end)
tool_s sum of tool execution time
overlap_s tool time that ran while the model was still streaming
(eager protocols; 0 for end-of-stream execution)
confirm_ms mean call open -> call confirmed (args complete)
wait_ms mean call confirmed -> execution start
ctl_errors contract errors (model left the CTL/XML grammar)
tokens prompt / completion / cached prompt tokens
"""
import csv
import json
import os
import statistics
import sys
from collections import defaultdict
FIELDS = [
"run", "fixture", "protocol", "provider", "model", "outcome", "pass",
"wall_s", "turn_s", "steps", "calls", "calls_failed", "llm_s", "tool_s",
"overlap_s", "confirm_ms", "wait_ms", "ctl_errors", "retries",
"prompt_tokens", "completion_tokens", "cached_tokens",
]
def load_jsonl(path):
rows = []
if not os.path.exists(path):
return rows
with open(path, encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line:
continue
try:
rows.append(json.loads(line))
except json.JSONDecodeError:
pass
return rows
def analyze(run_dir):
with open(os.path.join(run_dir, "result.json"), encoding="utf-8") as f:
res = json.load(f)
if res.get("outcome") in ("baseline_only", "unexpected_green_fixture"):
return None
m = load_jsonl(os.path.join(run_dir, "metrics.jsonl"))
steps = [r for r in m if r.get("type") == "step"]
calls = [r for r in m if r.get("type") == "call"]
turns = [r for r in m if r.get("type") == "turn"]
end_of = {(s["turn"], s["step"]): s.get("t_stream_end", 0) for s in steps}
overlap = 0.0
confirm, wait = [], []
tool_ms = 0.0
for c in calls:
start, end = c.get("t_exec_start", -1), c.get("t_exec_end", -1)
if start >= 0 and end >= start:
tool_ms += end - start
stream_end = end_of.get((c["turn"], c["step"]))
if stream_end is not None and start < stream_end:
overlap += min(end, stream_end) - start
if c.get("t_call_open", -1) >= 0 and c.get("t_call_ready", -1) >= 0:
confirm.append(c["t_call_ready"] - c["t_call_open"])
if c.get("t_call_ready", -1) >= 0 and start >= 0:
wait.append(start - c["t_call_ready"])
model = res.get("model", "")
if model in ("", "default") and steps:
model = steps[0].get("model", model)
return {
"run": res.get("run", os.path.basename(run_dir)),
"fixture": res.get("fixture", ""),
"protocol": res.get("protocol", ""),
"provider": res.get("provider", ""),
"model": model,
"outcome": res.get("outcome", ""),
"pass": 1 if res.get("outcome") == "pass" else 0,
"wall_s": res.get("wall_s", 0),
"turn_s": round(sum(t.get("t_total", 0) for t in turns) / 1000, 3),
"steps": len(steps),
"calls": len(calls),
"calls_failed": sum(1 for c in calls if c.get("exit", 0) != 0
or c.get("status") != "ok"),
"llm_s": round(sum(s.get("t_stream_end", 0) for s in steps) / 1000, 3),
"tool_s": round(tool_ms / 1000, 3),
"overlap_s": round(overlap / 1000, 3),
"confirm_ms": round(statistics.mean(confirm), 1) if confirm else "",
"wait_ms": round(statistics.mean(wait), 1) if wait else "",
"ctl_errors": sum(1 for s in steps if s.get("parse_error")),
"retries": sum(s.get("retries", 0) for s in steps),
"prompt_tokens": sum(s.get("prompt_tokens", 0) for s in steps),
"completion_tokens": sum(s.get("completion_tokens", 0) for s in steps),
"cached_tokens": sum(s.get("cached_tokens", 0) for s in steps),
}
def mean(rows, key, nd=2):
vals = [r[key] for r in rows if r[key] != ""]
if not vals:
return "-"
v = statistics.mean(vals)
return int(round(v)) if nd == 0 else round(v, nd)
def table(rows, keys, title):
groups = defaultdict(list)
for r in rows:
groups[tuple(r[k] for k in keys)].append(r)
cols = keys + ["n", "pass%", "steps", "wall_s", "llm_s", "tool_s",
"overlap_s", "wait_ms", "ctl_err", "tok_in", "tok_out"]
out = [f"### {title}", "", "| " + " | ".join(cols) + " |",
"|" + "---|" * len(cols)]
for g in sorted(groups):
rs = groups[g]
n = len(rs)
cells = list(g) + [
n,
round(100.0 * sum(r["pass"] for r in rs) / n),
mean(rs, "steps", 1),
mean(rs, "wall_s"),
mean(rs, "llm_s"),
mean(rs, "tool_s"),
mean(rs, "overlap_s"),
mean(rs, "wait_ms", 0),
mean(rs, "ctl_errors", 2),
mean(rs, "prompt_tokens", 0),
mean(rs, "completion_tokens", 0),
]
out.append("| " + " | ".join(str(c) for c in cells) + " |")
out.append("")
return "\n".join(out)
def main(argv):
if len(argv) < 2:
print(__doc__)
return 64
root = argv[1]
md_path = os.path.join(root, "summary.md")
csv_path = os.path.join(root, "results.csv")
if "--md" in argv:
md_path = argv[argv.index("--md") + 1]
if "--csv" in argv:
csv_path = argv[argv.index("--csv") + 1]
rows = []
for name in sorted(os.listdir(root)):
d = os.path.join(root, name)
if os.path.isfile(os.path.join(d, "result.json")):
r = analyze(d)
if r:
rows.append(r)
if not rows:
print(f"no runs under {root}")
return 1
with open(csv_path, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=FIELDS)
w.writeheader()
w.writerows(rows)
md = [f"## Results ({len(rows)} runs)", "",
"wait_ms = call confirmed -> execution start (~0 for eager "
"protocols, large when buffered until stream end). overlap_s = "
"tool time hidden behind generation.", "",
table(rows, ["model", "protocol"], "By model x protocol"),
table(rows, ["model", "protocol", "fixture"],
"By model x protocol x fixture")]
text = "\n".join(md)
with open(md_path, "w", encoding="utf-8") as f:
f.write(text)
print(text)
print(f"wrote {csv_path} and {md_path}")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))
+14
View File
@@ -0,0 +1,14 @@
# NixOS / nix users: `nix-shell` then `make && make check`.
{ pkgs ? import <nixpkgs> { } }:
pkgs.mkShell {
packages = with pkgs; [
gcc
gnumake
cmake
pkg-config
curl.dev
ncurses
python3
];
}
+1663 -978
View File
File diff suppressed because it is too large Load Diff
+132
View File
@@ -1,5 +1,8 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#endif
#include "cJSON.h"
#include "cst_agent.h" #include "cst_agent.h"
#include "os_compat.h" #include "os_compat.h"
@@ -38,6 +41,16 @@ int cst_agent_store_ensure(const char *workspace) {
return -1; return -1;
if (os_mkdir(sess, 0755) != 0) if (os_mkdir(sess, 0755) != 0)
return -1; return -1;
/* keep sessions, undo snapshots and exports out of git */
char gi[OS_PATH_MAX];
if (os_path_join(gi, sizeof(gi), base, ".gitignore") == 0 &&
OS_ACCESS(gi, OS_F_OK) != 0) {
FILE *fp = fopen(gi, "w");
if (fp) {
fputs("*\n", fp);
fclose(fp);
}
}
return 0; return 0;
} }
@@ -187,3 +200,122 @@ int cst_agent_store_list(const char *workspace, char ids[][128], int max_ids) {
free(ents); free(ents);
return out_n; return out_n;
} }
static int side_path(const char *workspace, const char *id, const char *ext,
char *out, size_t out_sz) {
char dir[OS_PATH_MAX], leaf[192];
if (!id || !id[0] || strchr(id, '/') || strchr(id, '\\') ||
cst_agent_store_dir(workspace, dir, sizeof(dir)) != 0)
return -1;
snprintf(leaf, sizeof(leaf), "%s%s", id, ext);
return os_path_join(out, out_sz, dir, leaf);
}
int cst_agent_store_title_set(const char *workspace, const char *id,
const char *title) {
char p[OS_PATH_MAX];
if (side_path(workspace, id, ".title", p, sizeof(p)) != 0)
return -1;
if (!title || !title[0])
return remove(p) == 0 || errno == ENOENT ? 0 : -1;
FILE *fp = fopen(p, "w");
if (!fp)
return -1;
for (const char *c = title; *c; c++)
fputc(*c == '\n' || *c == '\r' ? ' ' : *c, fp);
fputc('\n', fp);
return fclose(fp) == 0 ? 0 : -1;
}
/* Cut to n bytes without splitting a UTF-8 sequence; newlines → spaces. */
static void clip_line(char *dst, size_t n, const char *src) {
size_t i = 0;
while (src[i] && i + 1 < n) {
dst[i] = (src[i] == '\n' || src[i] == '\r' || src[i] == '\t') ? ' '
: src[i];
i++;
}
while (i > 0 && src[i] && ((unsigned char)src[i] & 0xC0) == 0x80)
i--;
dst[i] = 0;
}
int cst_agent_store_info(const char *workspace, const char *id,
cst_session_info_t *info) {
if (!info)
return -1;
memset(info, 0, sizeof(*info));
snprintf(info->id, sizeof(info->id), "%s", id ? id : "");
char p[OS_PATH_MAX];
if (side_path(workspace, id, ".jsonl", p, sizeof(p)) != 0)
return -1;
struct stat st;
if (stat(p, &st) != 0)
return -1;
info->mtime = (long long)st.st_mtime;
char tp[OS_PATH_MAX];
if (side_path(workspace, id, ".title", tp, sizeof(tp)) == 0) {
FILE *tf = fopen(tp, "r");
if (tf) {
if (fgets(info->title, sizeof(info->title), tf)) {
size_t n = strlen(info->title);
while (n && (info->title[n - 1] == '\n' || info->title[n - 1] == '\r'))
info->title[--n] = 0;
}
fclose(tf);
}
}
FILE *fp = fopen(p, "r");
if (!fp)
return 0;
size_t cap = 1 << 16;
char *line = malloc(cap);
int have_first = 0;
while (line && fgets(line, (int)cap, fp)) {
size_t n = strlen(line);
if (n == cap - 1 && line[n - 1] != '\n') {
/* very long line: skip the rest, it is not the first user line */
int ch;
while ((ch = fgetc(fp)) != EOF && ch != '\n')
;
info->messages++;
continue;
}
if (n <= 1)
continue;
info->messages++;
if (have_first)
continue;
cJSON *o = cJSON_Parse(line);
const cJSON *r = cJSON_GetObjectItemCaseSensitive(o, "role");
const cJSON *c = cJSON_GetObjectItemCaseSensitive(o, "content");
if (cJSON_IsString(r) && strcmp(r->valuestring, "user") == 0 &&
cJSON_IsString(c) && c->valuestring[0]) {
clip_line(info->first_user, sizeof(info->first_user), c->valuestring);
have_first = 1;
}
cJSON_Delete(o);
}
free(line);
fclose(fp);
return 0;
}
int cst_agent_store_delete(const char *workspace, const char *id) {
char p[OS_PATH_MAX];
if (side_path(workspace, id, ".jsonl", p, sizeof(p)) != 0)
return -1;
if (remove(p) != 0)
return -1;
if (side_path(workspace, id, ".title", p, sizeof(p)) == 0)
remove(p);
char cur[128];
if (cst_agent_store_get_current(workspace, cur, sizeof(cur)) == 0 &&
strcmp(cur, id) == 0) {
char dir[OS_PATH_MAX];
if (cst_agent_store_dir(workspace, dir, sizeof(dir)) == 0 &&
os_path_join(p, sizeof(p), dir, "CURRENT") == 0)
remove(p);
}
return 0;
}
+133 -81
View File
@@ -1,4 +1,6 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#endif
#include "cst_agent.h" #include "cst_agent.h"
#include "os_compat.h" #include "os_compat.h"
@@ -11,10 +13,13 @@
#ifdef _WIN32 #ifdef _WIN32
#include <io.h> #include <io.h>
#else #else
#include <fcntl.h>
#include <termios.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
/* Config dir via os_compat: ~/.config/cst-agent or %APPDATA%\cst-agent */ /* Config dir via os_compat: $XDG_CONFIG_HOME/cst-agent (~/.config/cst-agent)
* or %APPDATA%\cst-agent */
static int credentials_path(char *out, size_t n) { static int credentials_path(char *out, size_t n) {
char dir[OS_PATH_MAX]; char dir[OS_PATH_MAX];
@@ -23,8 +28,24 @@ static int credentials_path(char *out, size_t n) {
return os_path_join(out, n, dir, "credentials"); return os_path_join(out, n, dir, "credentials");
} }
int cst_creds_load_api_key(char *out, size_t out_sz) { static void chomp(char *s) {
if (!out || out_sz < 2) size_t len = strlen(s);
while (len > 0 && (s[len - 1] == '\n' || s[len - 1] == '\r' ||
s[len - 1] == ' ' || s[len - 1] == '\t'))
s[--len] = 0;
}
static int same_base(const char *a, const char *b) {
size_t la = strlen(a), lb = strlen(b);
while (la > 0 && a[la - 1] == '/')
la--;
while (lb > 0 && b[lb - 1] == '/')
lb--;
return la == lb && strncmp(a, b, la) == 0;
}
int cst_creds_load(const char *provider, char *out, size_t out_sz) {
if (!provider || !provider[0] || !out || out_sz < 2)
return -1; return -1;
out[0] = 0; out[0] = 0;
char path[OS_PATH_MAX]; char path[OS_PATH_MAX];
@@ -33,108 +54,139 @@ int cst_creds_load_api_key(char *out, size_t out_sz) {
FILE *fp = fopen(path, "r"); FILE *fp = fopen(path, "r");
if (!fp) if (!fp)
return -1; return -1;
char want[64];
snprintf(want, sizeof(want), "%s.api_key=", provider);
size_t wl = strlen(want);
/* Old single-key files ("api_key=" + "api_base=") belong to the provider
* whose base URL matches; without a base they were MiniMax keys. */
char legacy_key[1024] = "", legacy_base[1024] = "";
char line[1024]; char line[1024];
int found = 0;
while (fgets(line, sizeof(line), fp)) { while (fgets(line, sizeof(line), fp)) {
if (strncmp(line, "api_key=", 8) != 0) chomp(line);
continue; if (strncmp(line, want, wl) == 0 && line[wl]) {
char *v = line + 8; snprintf(out, out_sz, "%s", line + wl);
size_t len = strlen(v); found = 1;
while (len > 0 && (v[len - 1] == '\n' || v[len - 1] == '\r')) break;
v[--len] = 0;
if (len == 0 || len + 1 > out_sz) {
fclose(fp);
return -1;
} }
memcpy(out, v, len + 1); if (strncmp(line, "api_key=", 8) == 0)
fclose(fp); snprintf(legacy_key, sizeof(legacy_key), "%s", line + 8);
return 0; else if (strncmp(line, "api_base=", 9) == 0)
snprintf(legacy_base, sizeof(legacy_base), "%s", line + 9);
} }
fclose(fp); fclose(fp);
if (found)
return 0;
if (legacy_key[0]) {
const cst_provider_t *p = cst_provider_find(provider);
int match = p && (legacy_base[0] ? same_base(legacy_base, p->api_base)
: strcmp(provider, "minimax") == 0);
if (match && strlen(legacy_key) < out_sz) {
snprintf(out, out_sz, "%s", legacy_key);
return 0;
}
}
return -1; return -1;
} }
int cst_creds_save_api_key(const char *key) { int cst_creds_save(const char *provider, const char *key) {
if (!key || !key[0]) if (!provider || !provider[0] || !key || !key[0] || strchr(key, '\n'))
return -1; return -1;
char dir[OS_PATH_MAX], path[OS_PATH_MAX]; char dir[OS_PATH_MAX], path[OS_PATH_MAX], tmp[OS_PATH_MAX + 8];
if (os_config_dir(dir, sizeof(dir)) != 0) if (os_config_dir(dir, sizeof(dir)) != 0 || os_mkdir_p(dir, 0700) != 0 ||
credentials_path(path, sizeof(path)) != 0)
return -1; return -1;
if (os_mkdir(dir, 0700) != 0) if (snprintf(tmp, sizeof(tmp), "%s.tmp", path) >= (int)sizeof(tmp))
return -1; return -1;
if (credentials_path(path, sizeof(path)) != 0)
char want[64];
snprintf(want, sizeof(want), "%s.api_key=", provider);
size_t wl = strlen(want);
#ifdef _WIN32
FILE *out = fopen(tmp, "w");
#else
/* never exists world-readable, even briefly */
int fd = open(tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
FILE *out = fd >= 0 ? fdopen(fd, "w") : NULL;
if (!out && fd >= 0)
close(fd);
#endif
if (!out)
return -1; return -1;
FILE *fp = fopen(path, "w"); FILE *in = fopen(path, "r");
if (!fp) if (in) {
char line[1024];
while (fgets(line, sizeof(line), in)) {
if (strncmp(line, want, wl) != 0)
fputs(line, out);
}
fclose(in);
}
fprintf(out, "%s%s\n", want, key);
if (fclose(out) != 0) {
remove(tmp);
return -1; return -1;
fprintf(fp, "api_key=%s\n", key); }
fprintf(fp, "api_base=https://api.minimax.cn/v1\n"); #ifdef _WIN32
fprintf(fp, "model=MiniMax-M2.7\n"); remove(path);
fclose(fp); #endif
if (rename(tmp, path) != 0) {
remove(tmp);
return -1;
}
(void)os_chmod_private(path); (void)os_chmod_private(path);
return 0; return 0;
} }
/* Zenity on Unix; stdin prompt elsewhere / when zenity missing. */ int cst_creds_resolve(const char *provider, char *out, size_t out_sz,
int cst_creds_prompt_api_key(char *out, size_t out_sz) { const char **from) {
if (from)
*from = NULL;
if (cst_creds_load(provider, out, out_sz) == 0 && out[0]) {
if (from)
*from = "saved";
return 0;
}
const cst_provider_t *p = cst_provider_find(provider);
const char *env = p ? getenv(p->key_env) : NULL;
if (env && env[0]) {
snprintf(out, out_sz, "%s", env);
if (from)
*from = p->key_env;
return 0;
}
out[0] = 0;
return -1;
}
int cst_creds_prompt_console(const char *provider, char *out, size_t out_sz) {
if (!out || out_sz < 2) if (!out || out_sz < 2)
return -1; return -1;
out[0] = 0; out[0] = 0;
fprintf(stderr, "API key for %s (input hidden, saved to the config dir): ",
provider ? provider : "?");
fflush(stderr);
#ifndef _WIN32 #ifndef _WIN32
FILE *fp = popen( struct termios old, quiet;
"zenity --password --title='CST-Agent / MiniMax' " int tty = isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &old) == 0;
"--text='请输入 MiniMax API Key(将保存到配置目录 credentials)' " if (tty) {
"2>/dev/null", quiet = old;
"r"); quiet.c_lflag &= ~(tcflag_t)ECHO;
if (fp) { tcsetattr(STDIN_FILENO, TCSAFLUSH, &quiet);
if (fgets(out, (int)out_sz, fp)) {
int st = pclose(fp);
size_t len = strlen(out);
while (len > 0 && (out[len - 1] == '\n' || out[len - 1] == '\r'))
out[--len] = 0;
if (st == 0 && len > 0)
return 0;
} else {
pclose(fp);
}
out[0] = 0;
} }
#endif #endif
char *got = fgets(out, (int)out_sz, stdin);
/* Fallback: console prompt (headless / Windows / no zenity). */ #ifndef _WIN32
fprintf(stderr, "CST-Agent: enter MiniMax API Key (saved to config dir): "); if (tty) {
fflush(stderr); tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
if (!fgets(out, (int)out_sz, stdin)) { fputc('\n', stderr);
}
#endif
if (!got) {
out[0] = 0; out[0] = 0;
return -1; return -1;
} }
size_t len = strlen(out); chomp(out);
while (len > 0 && (out[len - 1] == '\n' || out[len - 1] == '\r')) return out[0] ? 0 : -1;
out[--len] = 0;
if (len == 0) {
out[0] = 0;
return -1;
}
return 0;
}
int cst_creds_ensure_api_key(char *out, size_t out_sz) {
const char *env = getenv("CST_API_KEY");
if (!env || !env[0])
env = getenv("MINIMAX_API_KEY");
if (!env || !env[0])
env = getenv("OPENAI_API_KEY");
if (env && env[0]) {
snprintf(out, out_sz, "%s", env);
return 0;
}
if (cst_creds_load_api_key(out, out_sz) == 0 && out[0])
return 0;
if (cst_creds_prompt_api_key(out, out_sz) != 0)
return -1;
if (cst_creds_save_api_key(out) != 0)
fprintf(stderr, "warning: failed to save credentials file\n");
else
fprintf(stderr, "saved API key to config directory\n");
return 0;
} }
+235 -100
View File
@@ -5,10 +5,13 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define CTL_MAX_TAG 32
typedef enum { typedef enum {
ST_TEXT = 0, /* outside a call; accumulate text lines */ ST_TEXT = 0, /* outside a call; accumulate text lines */
ST_LINE, /* collecting a line (call/arg/end or text) */ ST_LINE, /* collecting a line (call/arg/end or text) */
ST_ARG_BODY, /* reading nbytes of arg body */ ST_ARG_BODY, /* reading nbytes of arg body */
ST_HEREDOC, /* reading heredoc lines until the tag line */
ST_ERROR ST_ERROR
} ctl_state_t; } ctl_state_t;
@@ -26,15 +29,43 @@ struct ctl_parser {
size_t arg_cap; size_t arg_cap;
size_t arg_got; size_t arg_got;
size_t arg_need; size_t arg_need;
char tag[CTL_MAX_TAG + 1];
size_t pending; size_t pending;
size_t pending_peak; ctl_parser_stats_t st;
char errbuf[256]; char errbuf[256];
/* last length-prefixed arg, for actionable contract-error messages */
char last_nkey[CTL_MAX_KEY];
size_t last_nbytes;
}; };
const char *ctl_error_kind_name(ctl_error_kind_t k) {
switch (k) {
case CTL_EK_NONE:
return "none";
case CTL_EK_BAD_CALL:
return "bad_call";
case CTL_EK_BAD_ARG:
return "bad_arg";
case CTL_EK_UNEXPECTED_LINE:
return "unexpected_line";
case CTL_EK_BODY_EOF:
return "body_eof";
case CTL_EK_HEREDOC_EOF:
return "heredoc_eof";
case CTL_EK_LINE_TOO_LONG:
return "line_too_long";
case CTL_EK_LIMIT:
return "limit";
case CTL_EK_OOM:
return "oom";
}
return "unknown";
}
static void bump_pending(ctl_parser_t *p, size_t n) { static void bump_pending(ctl_parser_t *p, size_t n) {
p->pending = n; p->pending = n;
if (n > p->pending_peak) if (n > p->st.pending_peak)
p->pending_peak = n; p->st.pending_peak = n;
} }
static void emit(ctl_parser_t *p, ctl_event_t *ev) { static void emit(ctl_parser_t *p, ctl_event_t *ev) {
@@ -42,9 +73,10 @@ static void emit(ctl_parser_t *p, ctl_event_t *ev) {
p->on_event(ev, p->userdata); p->on_event(ev, p->userdata);
} }
static void fail(ctl_parser_t *p, const char *msg) { static void fail(ctl_parser_t *p, ctl_error_kind_t kind, const char *msg) {
snprintf(p->errbuf, sizeof(p->errbuf), "%s", msg); snprintf(p->errbuf, sizeof(p->errbuf), "%s", msg);
p->state = ST_ERROR; p->state = ST_ERROR;
p->st.error_kind = kind;
ctl_event_t ev = {0}; ctl_event_t ev = {0};
ev.type = CTL_EV_ERROR; ev.type = CTL_EV_ERROR;
ev.error = p->errbuf; ev.error = p->errbuf;
@@ -70,6 +102,7 @@ static void reset_arg_body(ctl_parser_t *p) {
p->arg_got = 0; p->arg_got = 0;
p->arg_need = 0; p->arg_need = 0;
p->arg_key[0] = 0; p->arg_key[0] = 0;
p->tag[0] = 0;
} }
ctl_parser_t *ctl_parser_create(ctl_on_event_fn on_event, void *userdata) { ctl_parser_t *ctl_parser_create(ctl_on_event_fn on_event, void *userdata) {
@@ -99,15 +132,25 @@ void ctl_parser_reset(ctl_parser_t *p) {
p->line_len = 0; p->line_len = 0;
p->in_call = 0; p->in_call = 0;
p->pending = 0; p->pending = 0;
/* keep pending_peak for experiment */ p->st.error_kind = CTL_EK_NONE;
p->last_nkey[0] = 0;
/* keep counters / pending_peak for experiments */
} }
size_t ctl_parser_pending(const ctl_parser_t *p) { size_t ctl_parser_pending(const ctl_parser_t *p) { return p ? p->pending : 0; }
return p ? p->pending : 0;
}
size_t ctl_parser_pending_peak(const ctl_parser_t *p) { size_t ctl_parser_pending_peak(const ctl_parser_t *p) {
return p ? p->pending_peak : 0; return p ? p->st.pending_peak : 0;
}
void ctl_parser_stats(const ctl_parser_t *p, ctl_parser_stats_t *out) {
if (!out)
return;
if (!p) {
memset(out, 0, sizeof(*out));
return;
}
*out = p->st;
} }
static int starts_with(const char *s, const char *pfx) { static int starts_with(const char *s, const char *pfx) {
@@ -132,6 +175,8 @@ static int parse_call_line(ctl_parser_t *p, const char *line) {
if (!*sp) if (!*sp)
return -1; return -1;
size_t nlen = strlen(sp); size_t nlen = strlen(sp);
while (nlen > 0 && (sp[nlen - 1] == ' ' || sp[nlen - 1] == '\t'))
nlen--;
if (nlen == 0 || nlen >= CTL_MAX_NAME) if (nlen == 0 || nlen >= CTL_MAX_NAME)
return -1; return -1;
for (size_t i = 0; i < nlen; i++) { for (size_t i = 0; i < nlen; i++) {
@@ -144,6 +189,7 @@ static int parse_call_line(ctl_parser_t *p, const char *line) {
memcpy(p->call.name, sp, nlen); memcpy(p->call.name, sp, nlen);
p->call.name[nlen] = 0; p->call.name[nlen] = 0;
p->in_call = 1; p->in_call = 1;
p->st.calls_open++;
ctl_event_t ev = {0}; ctl_event_t ev = {0};
ev.type = CTL_EV_CALL_OPEN; ev.type = CTL_EV_CALL_OPEN;
ev.call = &p->call; ev.call = &p->call;
@@ -151,12 +197,49 @@ static int parse_call_line(ctl_parser_t *p, const char *line) {
return 0; return 0;
} }
static int finish_arg(ctl_parser_t *p); static int finish_arg(ctl_parser_t *p) {
int i = p->call.nargs;
snprintf(p->call.args[i].key, CTL_MAX_KEY, "%s", p->arg_key);
if (!p->arg_buf) {
p->arg_buf = malloc(1);
if (!p->arg_buf)
return -1;
}
p->arg_buf[p->arg_got] = 0;
p->call.args[i].value = p->arg_buf;
p->call.args[i].value_len = p->arg_got;
p->arg_buf = NULL; /* ownership moved */
p->arg_cap = 0;
p->call.nargs++;
p->arg_need = 0;
p->arg_got = 0;
p->arg_key[0] = 0;
p->tag[0] = 0;
p->state = ST_LINE;
bump_pending(p, 0);
ctl_event_t ev = {0};
ev.type = CTL_EV_ARG;
ev.call = &p->call;
emit(p, &ev);
return 0;
}
static int valid_tag(const char *t, size_t n) {
if (n == 0 || n > CTL_MAX_TAG)
return 0;
for (size_t i = 0; i < n; i++) {
if (!(isalnum((unsigned char)t[i]) || t[i] == '_'))
return 0;
}
return 1;
}
/* Returns 0 ok, -1 contract error, -2 oom, -3 limit. */
static int parse_arg_header(ctl_parser_t *p, const char *line) { static int parse_arg_header(ctl_parser_t *p, const char *line) {
/* Forms: /* Forms:
* arg <key> <nbytes> → length-prefixed body follows * arg <key> <nbytes> → length-prefixed body follows
* arg <key> <value...> → value is rest of line (line-mode, confirmed now) * arg <key> <<TAG → heredoc body until a line equal to TAG
* arg <key> <value...> → value is rest of line (confirmed now)
*/ */
const char *s = line + 4; const char *s = line + 4;
while (*s == ' ' || *s == '\t') while (*s == ' ' || *s == '\t')
@@ -174,78 +257,81 @@ static int parse_arg_header(ctl_parser_t *p, const char *line) {
if (!*sp) if (!*sp)
return -1; return -1;
if (p->call.nargs >= CTL_MAX_ARGS) if (p->call.nargs >= CTL_MAX_ARGS)
return -1; return -3;
char *end = NULL;
unsigned long nbytes = strtoul(sp, &end, 10);
int length_prefixed = (end != sp && (*end == '\0' || *end == ' ' || *end == '\t'));
if (length_prefixed && end && *end != '\0') {
while (*end == ' ' || *end == '\t')
end++;
if (*end != '\0')
length_prefixed = 0; /* e.g. "9foo" or value starting with digits+text */
}
memcpy(p->arg_key, s, klen); memcpy(p->arg_key, s, klen);
p->arg_key[klen] = 0; p->arg_key[klen] = 0;
free(p->arg_buf);
p->arg_buf = NULL;
p->arg_cap = 0;
p->arg_got = 0;
if (sp[0] == '<' && sp[1] == '<') {
const char *t = sp + 2;
while (*t == ' ')
t++;
size_t tl = strlen(t);
while (tl > 0 && (t[tl - 1] == ' ' || t[tl - 1] == '\t'))
tl--;
if (tl >= 2 && (t[0] == '\'' || t[0] == '"') && t[tl - 1] == t[0]) {
t++;
tl -= 2;
}
if (!valid_tag(t, tl))
return -1;
memcpy(p->tag, t, tl);
p->tag[tl] = 0;
p->state = ST_HEREDOC;
p->st.args_heredoc++;
bump_pending(p, 0);
return 0;
}
char *end = NULL;
unsigned long nbytes = strtoul(sp, &end, 10);
int length_prefixed =
(end != sp && isdigit((unsigned char)sp[0]) &&
(*end == '\0' || *end == ' ' || *end == '\t'));
if (length_prefixed && *end != '\0') {
while (*end == ' ' || *end == '\t')
end++;
if (*end != '\0')
length_prefixed = 0; /* "9 apples" is an inline value */
}
if (!length_prefixed) { if (!length_prefixed) {
/* line-mode: entire remainder is the value */
size_t vlen = strlen(sp); size_t vlen = strlen(sp);
if (vlen > CTL_MAX_ARG_BYTES) if (vlen > CTL_MAX_ARG_BYTES)
return -1; return -3;
free(p->arg_buf);
p->arg_buf = malloc(vlen + 1); p->arg_buf = malloc(vlen + 1);
if (!p->arg_buf) if (!p->arg_buf)
return -2; return -2;
memcpy(p->arg_buf, sp, vlen); memcpy(p->arg_buf, sp, vlen);
p->arg_buf[vlen] = 0;
p->arg_need = vlen;
p->arg_got = vlen; p->arg_got = vlen;
p->arg_cap = vlen; p->arg_cap = vlen;
finish_arg(p); p->st.args_inline++;
return 0; return finish_arg(p) == 0 ? 0 : -2;
} }
if (nbytes > CTL_MAX_ARG_BYTES) if (nbytes > CTL_MAX_ARG_BYTES)
return -1; return -3;
p->arg_need = (size_t)nbytes; p->arg_need = (size_t)nbytes;
p->arg_got = 0; p->st.args_nbytes++;
free(p->arg_buf); snprintf(p->last_nkey, sizeof(p->last_nkey), "%s", p->arg_key);
p->arg_buf = NULL; p->last_nbytes = p->arg_need;
p->arg_cap = 0;
if (p->arg_need > 0) { if (p->arg_need > 0) {
p->arg_buf = malloc(p->arg_need + 1); p->arg_buf = malloc(p->arg_need + 1);
if (!p->arg_buf) if (!p->arg_buf)
return -2; return -2;
p->arg_cap = p->arg_need; p->arg_cap = p->arg_need;
} }
if (p->arg_need == 0)
return finish_arg(p) == 0 ? 0 : -2;
p->state = ST_ARG_BODY; p->state = ST_ARG_BODY;
bump_pending(p, p->arg_need); bump_pending(p, p->arg_need);
return 0; return 0;
} }
static int finish_arg(ctl_parser_t *p) {
int i = p->call.nargs;
snprintf(p->call.args[i].key, CTL_MAX_KEY, "%s", p->arg_key);
p->call.args[i].value = p->arg_buf;
p->call.args[i].value_len = p->arg_got;
if (p->arg_buf)
p->arg_buf[p->arg_got] = 0;
p->arg_buf = NULL; /* ownership moved */
p->call.nargs++;
p->arg_need = 0;
p->arg_got = 0;
p->arg_key[0] = 0;
p->state = ST_LINE;
bump_pending(p, 0);
ctl_event_t ev = {0};
ev.type = CTL_EV_ARG;
ev.call = &p->call;
emit(p, &ev);
return 0;
}
static void emit_text_line(ctl_parser_t *p, const char *line, size_t len) { static void emit_text_line(ctl_parser_t *p, const char *line, size_t len) {
/* include newline as confirmed text */ /* include newline as confirmed text */
char tmp[CTL_MAX_LINE + 1]; char tmp[CTL_MAX_LINE + 1];
@@ -262,21 +348,55 @@ static void emit_text_line(ctl_parser_t *p, const char *line, size_t len) {
} }
static int is_fence_line(const char *line) { static int is_fence_line(const char *line) {
if (strncmp(line, "```", 3) == 0) return strncmp(line, "```", 3) == 0 || strncmp(line, "~~~", 3) == 0;
return 1; }
if (strncmp(line, "~~~", 3) == 0)
return 1; static void emit_ready(ctl_parser_t *p) {
ctl_event_t ev = {0};
ev.type = CTL_EV_CALL_READY;
ev.call = &p->call;
p->st.calls_ready++;
emit(p, &ev);
clear_call_args(&p->call);
p->in_call = 0;
bump_pending(p, 0);
p->state = ST_LINE;
}
static int heredoc_line(ctl_parser_t *p, const char *line, size_t len) {
if (strcmp(line, p->tag) == 0)
return finish_arg(p) == 0 ? 0 : -1;
size_t need = p->arg_got + len + 1;
if (need > CTL_MAX_ARG_BYTES) {
fail(p, CTL_EK_LIMIT, "heredoc body too large");
return -1;
}
if (need + 1 > p->arg_cap) {
size_t nc = p->arg_cap ? p->arg_cap * 2 : 256;
while (nc < need + 1)
nc *= 2;
char *nb = realloc(p->arg_buf, nc);
if (!nb) {
fail(p, CTL_EK_OOM, "oom");
return -1;
}
p->arg_buf = nb;
p->arg_cap = nc;
}
memcpy(p->arg_buf + p->arg_got, line, len);
p->arg_got += len;
p->arg_buf[p->arg_got++] = '\n';
bump_pending(p, 0);
return 0; return 0;
} }
static int handle_line(ctl_parser_t *p, const char *line) { static int handle_line(ctl_parser_t *p, const char *line) {
/* Ignore markdown fences so models can wrap CTL in code blocks. */ /* Ignore markdown fences so models can wrap CTL in code blocks. */
if (is_fence_line(line)) { if (is_fence_line(line) && !p->in_call) {
bump_pending(p, 0); bump_pending(p, 0);
return 0; return 0;
} }
/* Ignore blank lines between CTL frames. */ if (line[0] == '\0' || (p->in_call && is_fence_line(line))) {
if (line[0] == '\0') {
bump_pending(p, 0); bump_pending(p, 0);
return 0; return 0;
} }
@@ -284,13 +404,13 @@ static int handle_line(ctl_parser_t *p, const char *line) {
if (!p->in_call) { if (!p->in_call) {
if (starts_with(line, "call ")) { if (starts_with(line, "call ")) {
if (parse_call_line(p, line) != 0) { if (parse_call_line(p, line) != 0) {
fail(p, "invalid call line"); fail(p, CTL_EK_BAD_CALL, "invalid call line");
return -1; return -1;
} }
p->last_nkey[0] = 0;
bump_pending(p, 0); bump_pending(p, 0);
return 0; return 0;
} }
/* confirmed text line */
emit_text_line(p, line, strlen(line)); emit_text_line(p, line, strlen(line));
bump_pending(p, 0); bump_pending(p, 0);
return 0; return 0;
@@ -300,29 +420,33 @@ static int handle_line(ctl_parser_t *p, const char *line) {
if (starts_with(line, "arg ")) { if (starts_with(line, "arg ")) {
int rc = parse_arg_header(p, line); int rc = parse_arg_header(p, line);
if (rc == -2) { if (rc == -2) {
fail(p, "oom"); fail(p, CTL_EK_OOM, "oom");
return -1;
}
if (rc == -3) {
fail(p, CTL_EK_LIMIT, "too many args or arg too large");
return -1; return -1;
} }
if (rc != 0) { if (rc != 0) {
fail(p, "invalid arg header"); fail(p, CTL_EK_BAD_ARG, "invalid arg header");
return -1; return -1;
} }
if (p->arg_need == 0)
finish_arg(p);
return 0; return 0;
} }
if (strcmp(line, "end") == 0) { if (strcmp(line, "end") == 0) {
ctl_event_t ev = {0}; emit_ready(p);
ev.type = CTL_EV_CALL_READY;
ev.call = &p->call;
emit(p, &ev);
clear_call_args(&p->call);
p->in_call = 0;
bump_pending(p, 0);
p->state = ST_LINE;
return 0; return 0;
} }
fail(p, "unexpected line inside call"); char msg[256];
if (p->last_nkey[0])
snprintf(msg, sizeof(msg),
"unexpected line \"%.40s\" inside call; arg %s declared %zu "
"bytes, so its real body was longer (byte count too small)",
line, p->last_nkey, p->last_nbytes);
else
snprintf(msg, sizeof(msg), "unexpected line \"%.40s\" inside call",
line);
fail(p, CTL_EK_UNEXPECTED_LINE, msg);
return -1; return -1;
} }
@@ -345,30 +469,30 @@ ctl_status_t ctl_parser_feed(ctl_parser_t *p, const char *data, size_t len) {
i += take; i += take;
bump_pending(p, p->arg_need - p->arg_got); bump_pending(p, p->arg_need - p->arg_got);
} }
if (p->arg_got == p->arg_need) { if (p->arg_got == p->arg_need && finish_arg(p) != 0) {
finish_arg(p); fail(p, CTL_EK_OOM, "oom");
/* continue with remaining bytes in LINE mode */ return CTL_ERR_NOMEM;
} }
continue; continue;
} }
/* line accumulation */ if (p->state != ST_HEREDOC)
p->state = ST_LINE; p->state = ST_LINE;
unsigned char c = (unsigned char)data[i++]; unsigned char c = (unsigned char)data[i++];
if (c == '\r') if (c == '\r')
continue; /* normalize CRLF */ continue; /* normalize CRLF */
if (c == '\n') { if (c == '\n') {
p->line[p->line_len] = 0; p->line[p->line_len] = 0;
if (handle_line(p, p->line) != 0) int rc = p->state == ST_HEREDOC ? heredoc_line(p, p->line, p->line_len)
return CTL_ERR_CONTRACT; : handle_line(p, p->line);
p->line_len = 0; p->line_len = 0;
if (p->state == ST_ARG_BODY && p->arg_need == 0) { if (rc != 0)
/* already finished empty arg */ return p->st.error_kind == CTL_EK_LIMIT ? CTL_ERR_LIMIT
} : CTL_ERR_CONTRACT;
continue; continue;
} }
if (p->line_len + 1 >= CTL_MAX_LINE) { if (p->line_len + 1 >= CTL_MAX_LINE) {
fail(p, "line too long"); fail(p, CTL_EK_LINE_TOO_LONG, "line too long");
return CTL_ERR_LIMIT; return CTL_ERR_LIMIT;
} }
p->line[p->line_len++] = (char)c; p->line[p->line_len++] = (char)c;
@@ -384,9 +508,25 @@ ctl_status_t ctl_parser_finish(ctl_parser_t *p) {
return CTL_ERR_CONTRACT; return CTL_ERR_CONTRACT;
/* Mid-argument body cannot be confirmed at EOF. */ /* Mid-argument body cannot be confirmed at EOF. */
if (p->state == ST_ARG_BODY) { if (p->state == ST_ARG_BODY) {
fail(p, "incomplete arg body at EOF"); char msg[200];
snprintf(msg, sizeof(msg),
"stream ended inside arg %s: declared %zu bytes but only %zu "
"arrived (byte count too large)",
p->arg_key, p->arg_need, p->arg_got);
fail(p, CTL_EK_BODY_EOF, msg);
return CTL_ERR_CONTRACT; return CTL_ERR_CONTRACT;
} }
if (p->state == ST_HEREDOC) {
p->line[p->line_len] = 0;
if (p->line_len > 0 && strcmp(p->line, p->tag) == 0) {
p->line_len = 0;
if (finish_arg(p) != 0)
return CTL_ERR_NOMEM;
} else {
fail(p, CTL_EK_HEREDOC_EOF, "stream ended before heredoc terminator");
return CTL_ERR_CONTRACT;
}
}
/* Trailing text without final newline is still confirmed text. */ /* Trailing text without final newline is still confirmed text. */
if (!p->in_call && p->line_len > 0) { if (!p->in_call && p->line_len > 0) {
p->line[p->line_len] = 0; p->line[p->line_len] = 0;
@@ -398,21 +538,16 @@ ctl_status_t ctl_parser_finish(ctl_parser_t *p) {
if (p->in_call) { if (p->in_call) {
if (p->line_len > 0) { if (p->line_len > 0) {
p->line[p->line_len] = 0; p->line[p->line_len] = 0;
/* leftover line must be end or fence/blank */
if (strcmp(p->line, "end") == 0 || is_fence_line(p->line) || if (strcmp(p->line, "end") == 0 || is_fence_line(p->line) ||
p->line[0] == '\0') { p->line[0] == '\0') {
p->line_len = 0; p->line_len = 0;
} else { } else {
fail(p, "unexpected trailing line in call at EOF"); fail(p, CTL_EK_UNEXPECTED_LINE,
"unexpected trailing line in call at EOF");
return CTL_ERR_CONTRACT; return CTL_ERR_CONTRACT;
} }
} }
ctl_event_t ev = {0}; emit_ready(p);
ev.type = CTL_EV_CALL_READY;
ev.call = &p->call;
emit(p, &ev);
clear_call_args(&p->call);
p->in_call = 0;
} }
bump_pending(p, 0); bump_pending(p, 0);
return CTL_OK; return CTL_OK;
+27 -127
View File
@@ -1,145 +1,45 @@
#include "cJSON.h"
#include "cst_agent.h" #include "cst_agent.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
/* Minimal JSON tool object: {"name":"...","arguments":{...}} /* JSON-buffer baseline: {"name":"...","arguments":{...}} is executable only
* Buffer-until-valid baseline (NOT bounded CST). */ * once cJSON can parse the whole object (NOT bounded CST: nothing inside an
* unfinished object is ever confirmed). */
static const char *skip_ws(const char *p, const char *end) {
while (p < end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t'))
p++;
return p;
}
/* Find matching closing brace/bracket starting at open pos. Returns end ptr or
* NULL if incomplete. Handles strings/escapes. */
static const char *match_obj(const char *start, const char *end) {
if (start >= end || *start != '{')
return NULL;
int depth = 0;
int in_str = 0;
for (const char *p = start; p < end; p++) {
if (in_str) {
if (*p == '\\' && p + 1 < end) {
p++;
continue;
}
if (*p == '"')
in_str = 0;
continue;
}
if (*p == '"') {
in_str = 1;
continue;
}
if (*p == '{')
depth++;
else if (*p == '}') {
depth--;
if (depth == 0)
return p + 1;
}
}
return NULL; /* incomplete */
}
static int extract_string(const char *obj, const char *end, const char *key,
char *out, size_t out_sz) {
char pat[128];
snprintf(pat, sizeof(pat), "\"%s\"", key);
const char *k = obj;
while (k && k < end) {
k = strstr(k, pat);
if (!k || k >= end)
return -1;
/* ensure key boundary */
const char *colon = k + strlen(pat);
colon = skip_ws(colon, end);
if (colon >= end || *colon != ':') {
k++;
continue;
}
colon++;
colon = skip_ws(colon, end);
if (colon >= end || *colon != '"')
return -1;
colon++;
size_t o = 0;
for (const char *p = colon; p < end && o + 1 < out_sz; p++) {
if (*p == '\\' && p + 1 < end) {
p++;
if (*p == 'n')
out[o++] = '\n';
else
out[o++] = *p;
continue;
}
if (*p == '"') {
out[o] = 0;
return 0;
}
out[o++] = *p;
}
return -1;
}
return -1;
}
int cst_json_try_parse_tool(const char *buf, size_t len, cst_json_tool_t *out) { int cst_json_try_parse_tool(const char *buf, size_t len, cst_json_tool_t *out) {
if (!buf || !out) if (!buf || !out)
return 0; return 0;
memset(out, 0, sizeof(*out)); memset(out, 0, sizeof(*out));
const char *end = buf + len; const char *p = buf, *end = buf + len;
const char *p = buf;
while (p < end) { while (p < end) {
p = skip_ws(p, end);
if (p >= end)
break;
if (*p != '{') { if (*p != '{') {
p++; p++;
continue; continue;
} }
const char *obj_end = match_obj(p, end); const char *obj_end = NULL;
if (!obj_end) cJSON *o = cJSON_ParseWithLengthOpts(p, (size_t)(end - p), &obj_end, 0);
return 0; /* incomplete JSON — starvation failure mode */ if (!o)
if (extract_string(p, obj_end, "name", out->name, sizeof(out->name)) != return 0; /* incomplete/invalid: the starvation failure mode */
0) { const cJSON *name = cJSON_GetObjectItemCaseSensitive(o, "name");
p++; const cJSON *args = cJSON_GetObjectItemCaseSensitive(o, "arguments");
continue; int ok = 0;
if (cJSON_IsString(name) && args &&
strlen(name->valuestring) < sizeof(out->name)) {
char *a = cJSON_IsString(args) ? NULL : cJSON_PrintUnformatted(args);
const char *av = a ? a : (cJSON_IsString(args) ? args->valuestring : "");
if (strlen(av) < sizeof(out->arguments_json)) {
snprintf(out->name, sizeof(out->name), "%s", name->valuestring);
snprintf(out->arguments_json, sizeof(out->arguments_json), "%s", av);
out->ok = ok = 1;
}
free(a);
} }
/* arguments: object as raw substring */ cJSON_Delete(o);
const char *akey = strstr(p, "\"arguments\""); if (ok)
if (!akey || akey >= obj_end) { return 1;
p = obj_end; p = obj_end && obj_end > p ? obj_end : p + 1;
continue;
}
const char *colon = strchr(akey + 11, ':');
if (!colon || colon >= obj_end)
return 0;
colon++;
colon = skip_ws(colon, obj_end);
if (colon >= obj_end)
return 0;
if (*colon == '{') {
const char *ae = match_obj(colon, obj_end);
if (!ae)
return 0;
size_t n = (size_t)(ae - colon);
if (n >= sizeof(out->arguments_json))
return 0;
memcpy(out->arguments_json, colon, n);
out->arguments_json[n] = 0;
} else if (*colon == '"') {
if (extract_string(akey, obj_end, "arguments", out->arguments_json,
sizeof(out->arguments_json)) != 0)
return 0;
} else {
p = obj_end;
continue;
}
out->ok = 1;
return 1;
} }
return 0; return 0;
} }
+631 -717
View File
File diff suppressed because it is too large Load Diff
+316 -56
View File
@@ -1,4 +1,5 @@
#include "cst_agent.h" #include "cst_agent.h"
#include "cst_tools.h"
#include "os_compat.h" #include "os_compat.h"
#include <curl/curl.h> #include <curl/curl.h>
@@ -6,30 +7,120 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <time.h>
#ifdef _WIN32 #ifdef _WIN32
#include <direct.h> #include <direct.h>
#include <io.h>
#else #else
#include <unistd.h> #include <unistd.h>
#endif #endif
static void usage(const char *argv0) { static void usage(const char *argv0) {
fprintf(stderr, fprintf(stderr,
"Usage: %s [--workspace DIR] [--session FILE] [--api-base URL]\n" "Usage: %s [options] (works in the current directory)\n"
" [--api-key KEY] [--model NAME] [--headless]\n" " -P, --provider NAME minimax | deepseek | openai (last used)\n"
" [--prompt TEXT] [--demo-ctl] [--ask-key]\n" " -m, --model NAME model (last used for the provider)\n"
" -e, --effort LEVEL low | medium | high | max (last used)\n"
" -w, --workspace DIR project directory (default: current)\n"
" -c, --continue reopen the last session (default)\n"
" -N, --new start a new session\n"
" -R, --resume ID open a session by id or unique prefix\n"
" -L, --list-sessions list sessions and exit\n"
" -y, --yolo never ask before shell / writes / web\n"
" -C, --confirm LIST shell,write,net (default) | none\n"
" -r, --protocol P native-eos (default) | native-inc | ctl |\n"
" ctl-heredoc | xml\n"
" -T, --tools PROFILE default (all tools) | minimal (paper set)\n"
" -n, --max-steps N model calls per turn (30)\n"
" -H, --headless -p TEXT run one prompt without the TUI\n"
" -a, --ask-key enter and save the provider's API key\n"
" -k, --api-key KEY use KEY for this run only (not saved)\n"
" -b, --api-base URL OpenAI-compatible base URL (…/v1)\n"
" -s, --session FILE explicit session file\n"
" -d, --demo-ctl local CTL parser demo (no API)\n"
"\n" "\n"
"Defaults: api-base=https://api.minimax.cn/v1 model=MiniMax-M2.7\n" "API keys: the TUI asks for a missing key and saves it per provider\n"
"Session: <workspace>/.agent/sessions/<id>.jsonl (CURRENT tracked)\n" " in the config dir (credentials, mode 0600); /key changes it.\n"
"API key: CST_API_KEY / OPENAI_API_KEY / MINIMAX_API_KEY\n" " Fallbacks: <provider env> (DEEPSEEK_API_KEY, ...), CST_API_KEY.\n"
"Base: CST_API_BASE / OPENAI_BASE_URL (OpenAI-compatible …/v1)\n" "Settings: provider, models and effort are remembered in\n"
"Model: CST_MODEL / OPENAI_MODEL\n" " settings.json next to the credentials.\n"
"Also: CST_MODELS, CST_AUTO_APPROVE=1, CST_SHELL, CST_EXE_DIR\n" "Env: CST_PROVIDER CST_MODEL CST_EFFORT CST_PROTOCOL CST_TOOLS\n"
"TUI: /new /session /model /compact /details /help\n" " CST_CONFIRM CST_MAX_STEPS CST_TOOL_OUT_CAP CST_CTX_WINDOW\n"
" Enter send · Ctrl+N newline · ↑↓ history · Esc cancel\n" " CST_CTX_TOKENS CST_AUTO_COMPACT=0 CST_SHELL_TIMEOUT\n"
" busy Enter queues · tool perm y/n/a=always · wheel scroll\n", " CST_METRICS=0 CST_RECORD_SSE CST_REPLAY_SSE CST_MODELS\n"
" CST_AUTO_APPROVE=1 CST_SHELL CST_NO_PROJECT_NOTES=1 NO_COLOR\n"
"TUI: type /help inside for commands and keys\n",
argv0); argv0);
} }
static int parse_confirm(const char *s, cst_agent_config_t *cfg) {
cfg->confirm_shell = cfg->confirm_write = cfg->confirm_net = 0;
if (!s || strcmp(s, "none") == 0 || !s[0])
return 0;
char buf[128];
snprintf(buf, sizeof(buf), "%s", s);
for (char *tok = strtok(buf, ", "); tok; tok = strtok(NULL, ", ")) {
if (strcmp(tok, "shell") == 0)
cfg->confirm_shell = 1;
else if (strcmp(tok, "write") == 0)
cfg->confirm_write = 1;
else if (strcmp(tok, "net") == 0)
cfg->confirm_net = 1;
else if (strcmp(tok, "all") == 0)
cfg->confirm_shell = cfg->confirm_write = cfg->confirm_net = 1;
else
return -1;
}
return 0;
}
static int list_sessions(const char *ws) {
static char ids[200][128];
int n = cst_agent_store_list(ws, ids, 200);
char cur[128] = "";
cst_agent_store_get_current(ws, cur, sizeof(cur));
if (n == 0) {
printf("no sessions under %s/.agent/sessions\n", ws);
return 0;
}
for (int i = 0; i < n; i++) {
cst_session_info_t in;
if (cst_agent_store_info(ws, ids[i], &in) != 0)
continue;
char when[32] = "?";
time_t t = (time_t)in.mtime;
struct tm tm;
os_localtime(&t, &tm);
strftime(when, sizeof(when), "%Y-%m-%d %H:%M", &tm);
printf("%c %-24s %s %4d msgs %s\n", strcmp(cur, ids[i]) == 0 ? '*' : ' ',
in.id, when, in.messages,
in.title[0] ? in.title : in.first_user);
}
return 0;
}
/* Exact id or unique prefix → id. */
static int resolve_session_id(const char *ws, const char *want, char *out,
size_t out_sz) {
static char ids[500][128];
int n = cst_agent_store_list(ws, ids, 500), hit = -1;
for (int i = 0; i < n; i++) {
if (strcmp(ids[i], want) == 0) {
hit = i;
break;
}
if (strncmp(ids[i], want, strlen(want)) == 0) {
if (hit >= 0)
return -2;
hit = i;
}
}
if (hit < 0)
return -1;
snprintf(out, out_sz, "%s", ids[hit]);
return 0;
}
static int g_ready; static int g_ready;
static ctl_tool_call_t g_call; static ctl_tool_call_t g_call;
@@ -104,32 +195,52 @@ int main(int argc, char **argv) {
snprintf(cwd, sizeof(cwd), "."); snprintf(cwd, sizeof(cwd), ".");
snprintf(cfg.workspace_root, sizeof(cfg.workspace_root), "%s", cwd); snprintf(cfg.workspace_root, sizeof(cfg.workspace_root), "%s", cwd);
cfg.session_path[0] = 0; /* filled after workspace opts via .agent/store */ cfg.session_path[0] = 0; /* filled after workspace opts via .agent/store */
/* OpenAI-compatible base URL aliases */ cfg.api_base[0] = 0;
const char *env_base = getenv("CST_API_BASE");
if (!env_base || !env_base[0])
env_base = getenv("OPENAI_BASE_URL");
if (!env_base || !env_base[0])
env_base = getenv("OPENAI_API_BASE");
const char *env_model = getenv("CST_MODEL");
if (!env_model || !env_model[0])
env_model = getenv("OPENAI_MODEL");
snprintf(cfg.api_base, sizeof(cfg.api_base), "%s",
env_base && env_base[0] ? env_base : "https://api.minimax.cn/v1");
cfg.api_key[0] = 0; cfg.api_key[0] = 0;
snprintf(cfg.model, sizeof(cfg.model), "%s", cfg.model[0] = 0;
env_model && env_model[0] ? env_model : "MiniMax-M2.7"); cfg.protocol = CST_PROTO_NATIVE_EOS;
const char *env_proto = getenv("CST_PROTOCOL");
if (env_proto && env_proto[0] &&
cst_protocol_parse(env_proto, &cfg.protocol) != 0) {
fprintf(stderr, "unknown CST_PROTOCOL=%s\n", env_proto);
return 1;
}
cfg.max_steps = CST_DEFAULT_MAX_STEPS;
const char *env_steps = getenv("CST_MAX_STEPS");
if (env_steps && atoi(env_steps) > 0)
cfg.max_steps = atoi(env_steps);
cfg.tool_out_cap = CST_DEFAULT_TOOL_OUT_CAP;
const char *env_cap = getenv("CST_TOOL_OUT_CAP");
if (env_cap && atol(env_cap) > 0)
cfg.tool_out_cap = (size_t)atol(env_cap);
const char *provider_name = getenv("CST_PROVIDER");
cfg.headless = 0; cfg.headless = 0;
cfg.confirm_shell = 1; cfg.confirm_shell = 1;
cfg.confirm_write = 1; cfg.confirm_write = 1;
cfg.confirm_net = 1;
cfg.auto_approve = 0; cfg.auto_approve = 0;
cfg.tool_profile = CST_TOOLS_DEFAULT;
const char *env_tools = getenv("CST_TOOLS");
if (env_tools && env_tools[0] &&
cst_tool_profile_parse(env_tools, &cfg.tool_profile) != 0) {
fprintf(stderr, "unknown CST_TOOLS=%s (default | minimal)\n", env_tools);
return 1;
}
if (getenv("CST_AUTO_APPROVE")) if (getenv("CST_AUTO_APPROVE"))
cfg.auto_approve = 1; cfg.auto_approve = 1;
const char *env_confirm = getenv("CST_CONFIRM");
if (env_confirm && parse_confirm(env_confirm, &cfg) != 0) {
fprintf(stderr, "bad CST_CONFIRM=%s (shell,write,net | none)\n",
env_confirm);
return 1;
}
int do_demo = 0; int do_demo = 0;
int force_ask_key = 0; int force_ask_key = 0;
int session_explicit = 0; int session_explicit = 0;
const char *prompt = NULL; const char *prompt = NULL;
int key_from_cli = 0; int new_session = 0, do_list = 0;
const char *resume_id = NULL;
static struct option opts[] = { static struct option opts[] = {
{"workspace", required_argument, 0, 'w'}, {"workspace", required_argument, 0, 'w'},
@@ -137,14 +248,26 @@ int main(int argc, char **argv) {
{"api-base", required_argument, 0, 'b'}, {"api-base", required_argument, 0, 'b'},
{"api-key", required_argument, 0, 'k'}, {"api-key", required_argument, 0, 'k'},
{"model", required_argument, 0, 'm'}, {"model", required_argument, 0, 'm'},
{"provider", required_argument, 0, 'P'},
{"protocol", required_argument, 0, 'r'},
{"max-steps", required_argument, 0, 'n'},
{"tools", required_argument, 0, 'T'},
{"headless", no_argument, 0, 'H'}, {"headless", no_argument, 0, 'H'},
{"prompt", required_argument, 0, 'p'}, {"prompt", required_argument, 0, 'p'},
{"demo-ctl", no_argument, 0, 'd'}, {"demo-ctl", no_argument, 0, 'd'},
{"ask-key", no_argument, 0, 'a'}, {"ask-key", no_argument, 0, 'a'},
{"new", no_argument, 0, 'N'},
{"continue", no_argument, 0, 'c'},
{"resume", required_argument, 0, 'R'},
{"list-sessions", no_argument, 0, 'L'},
{"yolo", no_argument, 0, 'y'},
{"confirm", required_argument, 0, 'C'},
{"effort", required_argument, 0, 'e'},
{"help", no_argument, 0, 'h'}, {"help", no_argument, 0, 'h'},
{0, 0, 0, 0}}; {0, 0, 0, 0}};
int c; int c;
while ((c = getopt_long(argc, argv, "w:s:b:k:m:Hp:dah", opts, NULL)) != -1) { while ((c = getopt_long(argc, argv, "w:s:b:k:m:P:r:n:T:Hp:dahNcR:LyC:e:", opts, NULL)) !=
-1) {
switch (c) { switch (c) {
case 'w': case 'w':
snprintf(cfg.workspace_root, sizeof(cfg.workspace_root), "%s", optarg); snprintf(cfg.workspace_root, sizeof(cfg.workspace_root), "%s", optarg);
@@ -158,11 +281,33 @@ int main(int argc, char **argv) {
break; break;
case 'k': case 'k':
snprintf(cfg.api_key, sizeof(cfg.api_key), "%s", optarg); snprintf(cfg.api_key, sizeof(cfg.api_key), "%s", optarg);
key_from_cli = 1;
break; break;
case 'm': case 'm':
snprintf(cfg.model, sizeof(cfg.model), "%s", optarg); snprintf(cfg.model, sizeof(cfg.model), "%s", optarg);
break; break;
case 'P':
provider_name = optarg;
break;
case 'r':
if (cst_protocol_parse(optarg, &cfg.protocol) != 0) {
fprintf(stderr, "unknown protocol: %s\n", optarg);
curl_global_cleanup();
return 1;
}
break;
case 'n':
cfg.max_steps = atoi(optarg);
if (cfg.max_steps <= 0)
cfg.max_steps = CST_DEFAULT_MAX_STEPS;
break;
case 'T':
if (cst_tool_profile_parse(optarg, &cfg.tool_profile) != 0) {
fprintf(stderr, "unknown tool profile: %s (default | minimal)\n",
optarg);
curl_global_cleanup();
return 1;
}
break;
case 'H': case 'H':
cfg.headless = 1; cfg.headless = 1;
break; break;
@@ -175,6 +320,38 @@ int main(int argc, char **argv) {
case 'a': case 'a':
force_ask_key = 1; force_ask_key = 1;
break; break;
case 'N':
new_session = 1;
break;
case 'c':
new_session = 0;
resume_id = NULL;
break;
case 'R':
resume_id = optarg;
break;
case 'L':
do_list = 1;
break;
case 'y':
cfg.auto_approve = 1;
break;
case 'C':
if (parse_confirm(optarg, &cfg) != 0) {
fprintf(stderr, "bad --confirm %s (shell,write,net | none)\n", optarg);
curl_global_cleanup();
return 1;
}
break;
case 'e':
if (!cst_effort_valid(optarg)) {
fprintf(stderr, "bad --effort %s (low | medium | high | max)\n",
optarg);
curl_global_cleanup();
return 1;
}
snprintf(cfg.effort, sizeof(cfg.effort), "%s", optarg);
break;
case 'h': case 'h':
usage(argv[0]); usage(argv[0]);
curl_global_cleanup(); curl_global_cleanup();
@@ -186,6 +363,63 @@ int main(int argc, char **argv) {
} }
} }
if (do_list) {
int rc = list_sessions(cfg.workspace_root);
curl_global_cleanup();
return rc;
}
/* Explicit option > environment > last used (settings.json) > preset. */
cst_settings_t saved;
cst_settings_load(&saved);
if ((!provider_name || !provider_name[0]) &&
cst_provider_find(saved.provider))
provider_name = saved.provider;
const cst_provider_t *prov = cst_provider_default();
if (provider_name && provider_name[0]) {
prov = cst_provider_find(provider_name);
if (!prov) {
fprintf(stderr, "unknown provider: %s\n", provider_name);
curl_global_cleanup();
return 1;
}
}
snprintf(cfg.provider, sizeof(cfg.provider), "%s", prov->name);
if (!cfg.effort[0]) {
const char *e = getenv("CST_EFFORT");
if (!cst_effort_valid(e))
e = saved.effort;
snprintf(cfg.effort, sizeof(cfg.effort), "%s",
cst_effort_valid(e) ? e : CST_DEFAULT_EFFORT);
}
if (!cfg.api_base[0]) {
const char *env_base = getenv("CST_API_BASE");
if (!env_base || !env_base[0])
env_base = getenv("OPENAI_BASE_URL");
if (!env_base || !env_base[0])
env_base = getenv("OPENAI_API_BASE");
snprintf(cfg.api_base, sizeof(cfg.api_base), "%s",
env_base && env_base[0] ? env_base : prov->api_base);
}
if (!cfg.model[0]) {
const char *env_model = getenv("CST_MODEL");
if (!env_model || !env_model[0])
env_model = getenv("OPENAI_MODEL");
if (!env_model || !env_model[0])
env_model = cst_settings_model(&saved, prov->name);
snprintf(cfg.model, sizeof(cfg.model), "%s",
env_model && env_model[0] ? env_model : prov->model);
}
/* --api-key > saved key > provider env > CST_API_KEY; the TUI asks for
* a missing key itself. */
if (!cfg.api_key[0] && !force_ask_key &&
cst_creds_resolve(prov->name, cfg.api_key, sizeof(cfg.api_key), NULL) !=
0) {
const char *k = getenv("CST_API_KEY");
if (k && k[0])
snprintf(cfg.api_key, sizeof(cfg.api_key), "%s", k);
}
/* trim trailing slash on api_base */ /* trim trailing slash on api_base */
size_t alen = strlen(cfg.api_base); size_t alen = strlen(cfg.api_base);
while (alen > 0 && cfg.api_base[alen - 1] == '/') while (alen > 0 && cfg.api_base[alen - 1] == '/')
@@ -196,7 +430,29 @@ int main(int argc, char **argv) {
cfg.auto_approve = 1; cfg.auto_approve = 1;
/* Default session under <workspace>/.agent/sessions/ (OpenCode-like). */ /* Default session under <workspace>/.agent/sessions/ (OpenCode-like). */
if (!session_explicit) { if (!session_explicit && (new_session || resume_id)) {
char id[128];
int rc = 0;
if (new_session) {
rc = cst_agent_store_ensure(cfg.workspace_root) != 0 ||
cst_agent_store_new_id(id, sizeof(id)) != 0
? -1
: 0;
} else {
rc = resolve_session_id(cfg.workspace_root, resume_id, id, sizeof(id));
if (rc != 0)
fprintf(stderr, "%s session: %s (see --list-sessions)\n",
rc == -2 ? "ambiguous" : "no such", resume_id);
}
if (rc != 0 || cst_agent_store_path_for_id(cfg.workspace_root, id,
cfg.session_path,
sizeof(cfg.session_path)) != 0) {
curl_global_cleanup();
return 1;
}
cst_agent_store_set_current(cfg.workspace_root, id);
fprintf(stderr, "session %s%s\n", id, new_session ? " (new)" : "");
} else if (!session_explicit) {
char id[128]; char id[128];
if (cst_agent_store_resolve_default(cfg.workspace_root, cfg.session_path, if (cst_agent_store_resolve_default(cfg.workspace_root, cfg.session_path,
sizeof(cfg.session_path), id, sizeof(cfg.session_path), id,
@@ -215,37 +471,41 @@ int main(int argc, char **argv) {
return rc; return rc;
} }
if (force_ask_key || !key_from_cli) { int need_console_key =
if (force_ask_key) { force_ask_key || (!cfg.api_key[0] && cfg.headless && isatty(0));
if (cst_creds_prompt_api_key(cfg.api_key, sizeof(cfg.api_key)) != 0) { if (need_console_key) {
fprintf(stderr, "API key prompt cancelled\n"); if (cst_creds_prompt_console(cfg.provider, cfg.api_key,
curl_global_cleanup(); sizeof(cfg.api_key)) != 0) {
return 1; fprintf(stderr, "no API key entered\n");
} curl_global_cleanup();
if (cst_creds_save_api_key(cfg.api_key) != 0) return 1;
fprintf(stderr, "warning: failed to save credentials\n");
else
fprintf(stderr, "saved API key to ~/.config/cst-agent/credentials\n");
/* --ask-key alone: save and exit unless running a turn/TUI */
if (!cfg.headless && !prompt) {
fprintf(stderr, "using %s model=%s\n", cfg.api_base, cfg.model);
curl_global_cleanup();
return 0;
}
} else if (!cfg.api_key[0]) {
if (cst_creds_ensure_api_key(cfg.api_key, sizeof(cfg.api_key)) != 0) {
fprintf(stderr, "no API key (set CST_API_KEY or use --ask-key)\n");
curl_global_cleanup();
return 1;
}
} }
} else if (key_from_cli) { char dir[1024] = "the config directory";
/* persist CLI-provided key so next launch can reuse */ os_config_dir(dir, sizeof(dir));
if (cst_creds_save_api_key(cfg.api_key) != 0) if (cst_creds_save(cfg.provider, cfg.api_key) != 0)
fprintf(stderr, "warning: failed to save credentials\n"); fprintf(stderr, "warning: failed to save credentials\n");
else
fprintf(stderr, "saved the %s API key to %s/credentials\n", cfg.provider,
dir);
/* --ask-key alone: save and exit */
if (force_ask_key && !cfg.headless && !prompt) {
curl_global_cleanup();
return 0;
}
}
if (!cfg.api_key[0] && cfg.headless) {
fprintf(stderr,
"no API key for %s: start cst-agent once to enter it, or run "
"cst-agent --ask-key --provider %s\n",
cfg.provider, cfg.provider);
curl_global_cleanup();
return 1;
} }
fprintf(stderr, "using %s model=%s\n", cfg.api_base, cfg.model); fprintf(stderr,
"using %s model=%s effort=%s protocol=%s tools=%s max_steps=%d\n",
cfg.api_base, cfg.model, cfg.effort, cst_protocol_name(cfg.protocol),
cst_tool_profile_name(cfg.tool_profile), cfg.max_steps);
cst_agent_t *agent = cst_agent_create(&cfg); cst_agent_t *agent = cst_agent_create(&cfg);
if (!agent) { if (!agent) {
+86
View File
@@ -0,0 +1,86 @@
#include "cst_metrics.h"
#include "cJSON.h"
#include "os_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
struct cst_metrics {
FILE *fp;
char path[OS_PATH_MAX];
};
cst_metrics_t *cst_metrics_open(const char *workspace, const char *session_path) {
const char *off = getenv("CST_METRICS");
if (off && strcmp(off, "0") == 0)
return NULL;
cst_metrics_t *m = calloc(1, sizeof(*m));
if (!m)
return NULL;
const char *override = getenv("CST_METRICS_PATH");
if (override && override[0]) {
snprintf(m->path, sizeof(m->path), "%s", override);
} else {
if (!workspace)
workspace = ".";
char dir[OS_PATH_MAX], agent[OS_PATH_MAX];
os_path_join(agent, sizeof(agent), workspace, ".agent");
os_mkdir(agent, 0755);
os_path_join(dir, sizeof(dir), agent, "metrics");
os_mkdir(dir, 0755);
const char *base = session_path ? session_path : "session.jsonl";
const char *s1 = strrchr(base, '/');
const char *s2 = strrchr(base, '\\');
if (s2 && (!s1 || s2 > s1))
s1 = s2;
if (s1)
base = s1 + 1;
char leaf[256];
snprintf(leaf, sizeof(leaf), "%s", base);
size_t n = strlen(leaf);
if (n > 6 && strcmp(leaf + n - 6, ".jsonl") == 0)
leaf[n - 6] = 0;
char file[300];
snprintf(file, sizeof(file), "%s.jsonl", leaf);
os_path_join(m->path, sizeof(m->path), dir, file);
}
m->fp = fopen(m->path, "a");
if (!m->fp) {
free(m);
return NULL;
}
return m;
}
void cst_metrics_close(cst_metrics_t *m) {
if (!m)
return;
if (m->fp)
fclose(m->fp);
free(m);
}
void cst_metrics_emit(cst_metrics_t *m, void *obj) {
cJSON *o = obj;
if (!o)
return;
if (m && m->fp) {
if (!cJSON_GetObjectItemCaseSensitive(o, "ts"))
cJSON_AddNumberToObject(o, "ts", (double)time(NULL));
char *txt = cJSON_PrintUnformatted(o);
if (txt) {
fputs(txt, m->fp);
fputc('\n', m->fp);
fflush(m->fp);
free(txt);
}
}
cJSON_Delete(o);
}
const char *cst_metrics_path(const cst_metrics_t *m) {
return m ? m->path : NULL;
}
+2
View File
@@ -329,6 +329,8 @@ static void strip_stray_closes(mm_scan_t *s) {
/* Process buffer: emit text before tools, parse complete tool blocks. */ /* Process buffer: emit text before tools, parse complete tool blocks. */
static void drain(mm_scan_t *s) { static void drain(mm_scan_t *s) {
if (!s->buf)
return;
for (;;) { for (;;) {
if (!s->in_tool) { if (!s->in_tool) {
strip_stray_closes(s); strip_stray_closes(s);
+77 -2
View File
@@ -6,6 +6,28 @@
#define MODEL_MAX_LEN 128 #define MODEL_MAX_LEN 128
static const cst_provider_t PROVIDERS[] = {
{"minimax", "https://api.minimax.cn/v1", "MiniMax-M2.7", "MINIMAX_API_KEY"},
{"deepseek", "https://api.deepseek.com/v1", "deepseek-flash",
"DEEPSEEK_API_KEY"},
{"openai", "https://api.openai.com/v1", "gpt-4.1-mini", "OPENAI_API_KEY"},
};
const cst_provider_t *cst_provider_find(const char *name) {
if (!name || !name[0])
return NULL;
for (size_t i = 0; i < sizeof(PROVIDERS) / sizeof(PROVIDERS[0]); i++) {
if (strcmp(PROVIDERS[i].name, name) == 0)
return &PROVIDERS[i];
}
return NULL;
}
const cst_provider_t *cst_provider_default(void) {
const cst_provider_t *p = cst_provider_find(getenv("CST_PROVIDER"));
return p ? p : &PROVIDERS[0];
}
static int already(char out[][MODEL_MAX_LEN], int n, const char *name) { static int already(char out[][MODEL_MAX_LEN], int n, const char *name) {
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (strcmp(out[i], name) == 0) if (strcmp(out[i], name) == 0)
@@ -44,7 +66,6 @@ int cst_models_list(const char *current, char out[][128], int max_out) {
p++; p++;
char save = *p; char save = *p;
*p = 0; *p = 0;
/* trim trailing spaces */
char *end = start + strlen(start); char *end = start + strlen(start);
while (end > start && (end[-1] == ' ' || end[-1] == '\t')) while (end > start && (end[-1] == ' ' || end[-1] == '\t'))
*--end = 0; *--end = 0;
@@ -54,10 +75,12 @@ int cst_models_list(const char *current, char out[][128], int max_out) {
p++; p++;
} }
} else { } else {
push(out, &n, max_out, "deepseek-flash");
push(out, &n, max_out, "deepseek-v4-pro");
push(out, &n, max_out, "MiniMax-M2.7"); push(out, &n, max_out, "MiniMax-M2.7");
push(out, &n, max_out, "MiniMax-M2.5"); push(out, &n, max_out, "MiniMax-M2.5");
push(out, &n, max_out, "gpt-4.1-mini");
push(out, &n, max_out, "gpt-4o"); push(out, &n, max_out, "gpt-4o");
push(out, &n, max_out, "gpt-4o-mini");
} }
if (current && current[0]) if (current && current[0])
@@ -65,3 +88,55 @@ int cst_models_list(const char *current, char out[][128], int max_out) {
return n; return n;
} }
/* Context window in tokens (CST_CTX_TOKENS overrides). Prefix match. */
long cst_model_context_tokens(const char *model) {
const char *e = getenv("CST_CTX_TOKENS");
if (e && atol(e) > 0)
return atol(e);
static const struct {
const char *prefix;
long tokens;
} T[] = {{"deepseek", 128000}, {"MiniMax", 196608}, {"minimax", 196608},
{"gpt-4.1", 1047576}, {"gpt-4o", 128000}, {"gpt-5", 400000},
{"o3", 200000}, {"o4", 200000}, {"claude", 200000},
{"qwen", 131072}, {"glm", 128000}, {"kimi", 131072}};
if (model) {
for (size_t i = 0; i < sizeof(T) / sizeof(T[0]); i++) {
if (strncmp(model, T[i].prefix, strlen(T[i].prefix)) == 0)
return T[i].tokens;
}
}
return 128000;
}
int cst_effort_valid(const char *effort) {
static const char *const ok[] = {"low", "medium", "high", "max"};
for (size_t i = 0; effort && i < sizeof(ok) / sizeof(ok[0]); i++) {
if (strcmp(effort, ok[i]) == 0)
return 1;
}
return 0;
}
const char *cst_effort_wire(const char *provider, const char *model,
const char *effort) {
if (!cst_effort_valid(effort) || !provider || !model)
return NULL;
if (strcmp(provider, "deepseek") == 0)
return effort;
/* OpenAI reasoning models take low | medium | high. */
if (strcmp(provider, "openai") == 0 &&
(strncmp(model, "o1", 2) == 0 || strncmp(model, "o3", 2) == 0 ||
strncmp(model, "o4", 2) == 0 || strncmp(model, "gpt-5", 5) == 0))
return strcmp(effort, "max") == 0 ? "high" : effort;
return NULL;
}
int cst_provider_count(void) {
return (int)(sizeof(PROVIDERS) / sizeof(PROVIDERS[0]));
}
const cst_provider_t *cst_provider_at(int i) {
return i >= 0 && i < cst_provider_count() ? &PROVIDERS[i] : NULL;
}
+219 -35
View File
@@ -1,8 +1,14 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#endif
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#include <stdint.h>
#endif
#include "os_compat.h" #include "os_compat.h"
#include <errno.h> #include <errno.h>
@@ -21,9 +27,12 @@
#include <windows.h> #include <windows.h>
#else #else
#include <fcntl.h> #include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <time.h>
#include <unistd.h> #include <unistd.h>
#endif #endif
@@ -146,6 +155,22 @@ int os_mkdir(const char *path, int mode) {
#endif #endif
} }
int os_mkdir_p(const char *path, int mode) {
if (!path || !path[0])
return -1;
char tmp[OS_PATH_MAX];
snprintf(tmp, sizeof(tmp), "%s", path);
for (char *p = tmp + 1; *p; p++) {
if (*p == '/' || *p == '\\') {
char c = *p;
*p = 0;
os_mkdir(tmp, mode);
*p = c;
}
}
return os_mkdir(tmp, mode);
}
int os_config_dir(char *out, size_t out_sz) { int os_config_dir(char *out, size_t out_sz) {
if (!out || out_sz < 8) if (!out || out_sz < 8)
return -1; return -1;
@@ -157,10 +182,15 @@ int os_config_dir(char *out, size_t out_sz) {
return -1; return -1;
int n = snprintf(out, out_sz, "%s\\cst-agent", base); int n = snprintf(out, out_sz, "%s\\cst-agent", base);
#else #else
const char *xdg = getenv("XDG_CONFIG_HOME");
const char *home = getenv("HOME"); const char *home = getenv("HOME");
if (!home || !home[0]) int n;
if (xdg && xdg[0] == '/')
n = snprintf(out, out_sz, "%s/cst-agent", xdg);
else if (home && home[0])
n = snprintf(out, out_sz, "%s/.config/cst-agent", home);
else
return -1; return -1;
int n = snprintf(out, out_sz, "%s/.config/cst-agent", home);
#endif #endif
if (n < 0 || (size_t)n >= out_sz) if (n < 0 || (size_t)n >= out_sz)
return -1; return -1;
@@ -258,7 +288,12 @@ int os_exe_dir(char *out, size_t out_sz) {
return 0; return 0;
#else #else
char buf[OS_PATH_MAX]; char buf[OS_PATH_MAX];
#ifdef __APPLE__
uint32_t asz = (uint32_t)sizeof(buf);
ssize_t n = _NSGetExecutablePath(buf, &asz) == 0 ? (ssize_t)strlen(buf) : -1;
#else
ssize_t n = readlink("/proc/self/exe", buf, sizeof(buf) - 1); ssize_t n = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
#endif
if (n >= 0) { if (n >= 0) {
buf[n] = 0; buf[n] = 0;
char *slash = strrchr(buf, '/'); char *slash = strrchr(buf, '/');
@@ -343,6 +378,77 @@ int os_resolve_shell(char *out, size_t out_sz) {
#endif #endif
} }
static volatile int *g_run_cancel;
static int g_run_timeout_s;
void os_set_cancel_flag(volatile int *flag) { g_run_cancel = flag; }
void os_set_run_timeout(int seconds) { g_run_timeout_s = seconds; }
static int run_timeout_s(void) {
if (g_run_timeout_s > 0)
return g_run_timeout_s;
const char *e = getenv("CST_SHELL_TIMEOUT");
if (e && atoi(e) > 0)
return atoi(e);
return 120;
}
double os_now_ms(void) {
#ifdef _WIN32
static LARGE_INTEGER freq;
LARGE_INTEGER now;
if (!freq.QuadPart)
QueryPerformanceFrequency(&freq);
QueryPerformanceCounter(&now);
return (double)now.QuadPart * 1000.0 / (double)freq.QuadPart;
#else
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (double)ts.tv_sec * 1000.0 + (double)ts.tv_nsec / 1e6;
#endif
}
/* Append to capture buffer, keeping at most max_out bytes. */
static int cap_append(char **buf, size_t *len, size_t *cap, size_t max_out,
const char *data, size_t n, int *truncated) {
if (*len >= max_out) {
*truncated = 1;
return 0;
}
if (*len + n > max_out) {
n = max_out - *len;
*truncated = 1;
}
if (*len + n + 1 > *cap) {
size_t nc = *cap ? *cap * 2 : 4096;
while (nc < *len + n + 1)
nc *= 2;
char *nb = realloc(*buf, nc);
if (!nb)
return -1;
*buf = nb;
*cap = nc;
}
memcpy(*buf + *len, data, n);
*len += n;
(*buf)[*len] = 0;
return 0;
}
static void cap_note(char **buf, size_t *len, size_t *cap, const char *note) {
size_t n = strlen(note);
if (*len + n + 1 > *cap) {
char *nb = realloc(*buf, *len + n + 1);
if (!nb)
return;
*buf = nb;
*cap = *len + n + 1;
}
memcpy(*buf + *len, note, n + 1);
*len += n;
}
#ifdef _WIN32 #ifdef _WIN32
int os_run_capture(const char *cwd, const char *const argv[], size_t max_out, int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
os_run_result_t *r) { os_run_result_t *r) {
@@ -396,7 +502,9 @@ int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
si.dwFlags = STARTF_USESTDHANDLES; si.dwFlags = STARTF_USESTDHANDLES;
si.hStdOutput = wr; si.hStdOutput = wr;
si.hStdError = wr; si.hStdError = wr;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); HANDLE nul = CreateFileA("NUL", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
&sa, OPEN_EXISTING, 0, NULL);
si.hStdInput = nul != INVALID_HANDLE_VALUE ? nul : GetStdHandle(STD_INPUT_HANDLE);
PROCESS_INFORMATION pi = {0}; PROCESS_INFORMATION pi = {0};
char *cmd_mut = _strdup(cmdline); char *cmd_mut = _strdup(cmdline);
@@ -409,6 +517,8 @@ int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
cwd && cwd[0] ? cwd : NULL, &si, &pi); cwd && cwd[0] ? cwd : NULL, &si, &pi);
free(cmd_mut); free(cmd_mut);
CloseHandle(wr); CloseHandle(wr);
if (nul != INVALID_HANDLE_VALUE)
CloseHandle(nul);
if (!ok) { if (!ok) {
CloseHandle(rd); CloseHandle(rd);
return -1; return -1;
@@ -422,30 +532,54 @@ int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
return -1; return -1;
} }
buf[0] = 0;
int truncated = 0, killed = 0;
double deadline = os_now_ms() + run_timeout_s() * 1000.0;
for (;;) { for (;;) {
char tmp[1024]; DWORD avail = 0;
DWORD n = 0; if (!PeekNamedPipe(rd, NULL, 0, NULL, &avail, NULL))
if (!ReadFile(rd, tmp, sizeof(tmp), &n, NULL) || n == 0) break; /* writer closed */
break; if (avail > 0) {
if (len + n + 1 > cap) { char tmp[4096];
size_t nc = cap * 2; DWORD n = 0;
if (nc < len + n + 1) if (!ReadFile(rd, tmp, sizeof(tmp), &n, NULL) || n == 0)
nc = len + n + 1;
char *nb = realloc(buf, nc);
if (!nb)
break; break;
buf = nb; cap_append(&buf, &len, &cap, max_out, tmp, n, &truncated);
cap = nc; continue;
} }
memcpy(buf + len, tmp, n); if (WaitForSingleObject(pi.hProcess, 50) == WAIT_OBJECT_0) {
len += n; /* drain what is left */
if (len >= max_out) while (PeekNamedPipe(rd, NULL, 0, NULL, &avail, NULL) && avail > 0) {
char tmp[4096];
DWORD n = 0;
if (!ReadFile(rd, tmp, sizeof(tmp), &n, NULL) || n == 0)
break;
cap_append(&buf, &len, &cap, max_out, tmp, n, &truncated);
}
break; break;
}
if ((g_run_cancel && *g_run_cancel) || os_now_ms() > deadline) {
killed = (g_run_cancel && *g_run_cancel) ? 2 : 1;
TerminateProcess(pi.hProcess, killed == 1 ? 124 : 130);
break;
}
} }
CloseHandle(rd); CloseHandle(rd);
WaitForSingleObject(pi.hProcess, INFINITE); WaitForSingleObject(pi.hProcess, INFINITE);
DWORD code = 1; DWORD code = 1;
GetExitCodeProcess(pi.hProcess, &code); GetExitCodeProcess(pi.hProcess, &code);
if (truncated)
cap_note(&buf, &len, &cap, "\n...(output truncated)\n");
if (killed == 1) {
char note[96];
snprintf(note, sizeof(note), "\n[cst: killed after %ds timeout]\n",
run_timeout_s());
cap_note(&buf, &len, &cap, note);
code = 124;
} else if (killed == 2) {
cap_note(&buf, &len, &cap, "\n[cst: cancelled]\n");
code = 130;
}
CloseHandle(pi.hProcess); CloseHandle(pi.hProcess);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
buf[len] = 0; buf[len] = 0;
@@ -473,7 +607,13 @@ int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
return -1; return -1;
} }
if (pid == 0) { if (pid == 0) {
setpgid(0, 0);
close(pipefd[0]); close(pipefd[0]);
int devnull = open("/dev/null", O_RDONLY);
if (devnull >= 0) {
dup2(devnull, STDIN_FILENO);
close(devnull);
}
dup2(pipefd[1], STDOUT_FILENO); dup2(pipefd[1], STDOUT_FILENO);
dup2(pipefd[1], STDERR_FILENO); dup2(pipefd[1], STDERR_FILENO);
close(pipefd[1]); close(pipefd[1]);
@@ -482,41 +622,85 @@ int os_run_capture(const char *cwd, const char *const argv[], size_t max_out,
execvp(argv[0], (char *const *)argv); execvp(argv[0], (char *const *)argv);
_exit(127); _exit(127);
} }
setpgid(pid, pid); /* also in parent: avoid racing the child */
close(pipefd[1]); close(pipefd[1]);
size_t cap = 4096, len = 0; size_t cap = 4096, len = 0;
char *buf = malloc(cap); char *buf = malloc(cap);
if (!buf) { if (!buf) {
close(pipefd[0]); close(pipefd[0]);
kill(-pid, SIGKILL);
waitpid(pid, NULL, 0); waitpid(pid, NULL, 0);
return -1; return -1;
} }
buf[0] = 0;
int truncated = 0, killed = 0;
int status = 0, reaped = 0, bg_killed = 0;
double reaped_at = 0, killed_at = 0;
double deadline = os_now_ms() + run_timeout_s() * 1000.0;
for (;;) { for (;;) {
if (len + 1024 > cap) { struct pollfd pfd = {pipefd[0], POLLIN, 0};
cap *= 2; int pr = poll(&pfd, 1, 100);
char *nb = realloc(buf, cap); if (pr < 0 && errno != EINTR)
if (!nb) break;
if (pr > 0) {
char tmp[4096];
ssize_t n = read(pipefd[0], tmp, sizeof(tmp));
if (n == 0)
break; /* every writer closed */
if (n > 0)
cap_append(&buf, &len, &cap, max_out, tmp, (size_t)n, &truncated);
else if (errno != EINTR && errno != EAGAIN)
break; break;
buf = nb;
} }
ssize_t n = read(pipefd[0], buf + len, 1024); double now = os_now_ms();
if (n < 0) { if (!reaped && waitpid(pid, &status, WNOHANG) == pid) {
if (errno == EINTR) reaped = 1;
continue; reaped_at = now;
break;
} }
if (n == 0) /* The shell exited but a background child still holds the pipe
break; * (`server &`): give it a moment, then end the group. */
len += (size_t)n; if (reaped && !killed && !bg_killed && now - reaped_at > 1000) {
if (len >= max_out) kill(-pid, SIGKILL);
bg_killed = 1;
killed_at = now;
}
/* Checked on every pass, so a chatty command still stops promptly. */
if (!reaped && !killed &&
((g_run_cancel && *g_run_cancel) || now > deadline)) {
killed = (g_run_cancel && *g_run_cancel) ? 2 : 1;
killed_at = now;
kill(-pid, SIGTERM);
}
if (killed && !reaped && now - killed_at > 1000)
kill(-pid, SIGKILL);
/* a writer that left the process group can keep the pipe open */
if ((killed || bg_killed) && now - killed_at > 3000)
break; break;
} }
close(pipefd[0]); close(pipefd[0]);
int status = 0; if (pid > 0 && !reaped) {
waitpid(pid, &status, 0); if (killed)
buf[len] = 0; kill(-pid, SIGKILL);
waitpid(pid, &status, 0);
}
if (truncated)
cap_note(&buf, &len, &cap, "\n...(output truncated)\n");
if (bg_killed)
cap_note(&buf, &len, &cap,
"\n[cst: background processes left running were stopped]\n");
r->out = buf; r->out = buf;
r->out_len = len; r->out_len = len;
r->exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : 1; r->exit_code = WIFEXITED(status) ? WEXITSTATUS(status) : 1;
if (killed == 1) {
char note[96];
snprintf(note, sizeof(note), "\n[cst: killed after %ds timeout]\n",
run_timeout_s());
cap_note(&r->out, &r->out_len, &cap, note);
r->exit_code = 124;
} else if (killed == 2) {
cap_note(&r->out, &r->out_len, &cap, "\n[cst: cancelled]\n");
r->exit_code = 130;
}
return 0; return 0;
} }
#endif #endif
+223
View File
@@ -0,0 +1,223 @@
/* Persistent "always allow" rules: <ws>/.agent/permissions.json
* {"tools": ["write_file"], "shell_prefixes": ["make", "git status"]} */
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#include "cst_perms.h"
#include "cst_util.h"
#include "os_compat.h"
#define PERM_MAX 64
struct cst_perms {
char path[OS_PATH_MAX];
char tools[PERM_MAX][64];
int ntools;
char prefixes[PERM_MAX][128];
int nprefixes;
};
static void load(cst_perms_t *p) {
FILE *fp = fopen(p->path, "rb");
if (!fp)
return;
cst_buf_t b = {0};
char tmp[4096];
size_t n;
while ((n = fread(tmp, 1, sizeof(tmp), fp)) > 0 && b.len < 256 * 1024)
cst_buf_append(&b, tmp, n);
fclose(fp);
cJSON *root = b.buf ? cJSON_Parse(b.buf) : NULL;
cst_buf_free(&b);
const cJSON *it = NULL;
cJSON_ArrayForEach(it, cJSON_GetObjectItemCaseSensitive(root, "tools")) {
if (cJSON_IsString(it) && p->ntools < PERM_MAX)
snprintf(p->tools[p->ntools++], 64, "%.63s", it->valuestring);
}
cJSON_ArrayForEach(it,
cJSON_GetObjectItemCaseSensitive(root, "shell_prefixes")) {
if (cJSON_IsString(it) && p->nprefixes < PERM_MAX && it->valuestring[0])
snprintf(p->prefixes[p->nprefixes++], 128, "%.127s", it->valuestring);
}
cJSON_Delete(root);
}
cst_perms_t *cst_perms_open(const char *workspace) {
cst_perms_t *p = calloc(1, sizeof(*p));
if (!p)
return NULL;
if (workspace)
snprintf(p->path, sizeof(p->path), "%s/.agent/permissions.json",
workspace);
load(p);
return p;
}
void cst_perms_close(cst_perms_t *p) { free(p); }
static int save(const cst_perms_t *p) {
if (!p->path[0])
return -1;
char dir[OS_PATH_MAX];
snprintf(dir, sizeof(dir), "%s", p->path);
char *sl = strrchr(dir, '/');
if (sl) {
*sl = 0;
os_mkdir_p(dir, 0755);
}
cJSON *root = cJSON_CreateObject();
cJSON *t = cJSON_AddArrayToObject(root, "tools");
for (int i = 0; i < p->ntools; i++)
cJSON_AddItemToArray(t, cJSON_CreateString(p->tools[i]));
cJSON *s = cJSON_AddArrayToObject(root, "shell_prefixes");
for (int i = 0; i < p->nprefixes; i++)
cJSON_AddItemToArray(s, cJSON_CreateString(p->prefixes[i]));
char *txt = cJSON_Print(root);
cJSON_Delete(root);
if (!txt)
return -1;
FILE *fp = fopen(p->path, "wb");
int rc = -1;
if (fp) {
rc = fputs(txt, fp) >= 0 && fputs("\n", fp) >= 0 ? 0 : -1;
if (fclose(fp) != 0)
rc = -1;
}
free(txt);
return rc;
}
/* Commands chained or substituted can hide anything behind an allowed
* prefix, so they always ask. */
static int has_shell_meta(const char *cmd) {
return strpbrk(cmd, ";&|`$<>\n\r(){}") != NULL;
}
int cst_perms_shell_allowed(const cst_perms_t *p, const char *cmd) {
if (!p || !cmd)
return 0;
while (*cmd == ' ' || *cmd == '\t')
cmd++;
if (!*cmd || has_shell_meta(cmd))
return 0;
for (int i = 0; i < p->nprefixes; i++) {
size_t n = strlen(p->prefixes[i]);
if (strncmp(cmd, p->prefixes[i], n) == 0 &&
(cmd[n] == 0 || cmd[n] == ' ' || cmd[n] == '\t'))
return 1;
}
return 0;
}
int cst_perms_tool_allowed(const cst_perms_t *p, const char *tool) {
if (!p || !tool)
return 0;
for (int i = 0; i < p->ntools; i++) {
if (strcmp(p->tools[i], tool) == 0)
return 1;
}
return 0;
}
int cst_perms_allow_tool(cst_perms_t *p, const char *tool) {
if (!p || !tool || !tool[0])
return -1;
if (cst_perms_tool_allowed(p, tool))
return 0;
if (p->ntools >= PERM_MAX)
return -1;
snprintf(p->tools[p->ntools++], 64, "%.63s", tool);
return save(p);
}
int cst_perms_allow_prefix(cst_perms_t *p, const char *prefix) {
if (!p || !prefix)
return -1;
while (*prefix == ' ')
prefix++;
if (!prefix[0] || has_shell_meta(prefix))
return -1;
for (int i = 0; i < p->nprefixes; i++) {
if (strcmp(p->prefixes[i], prefix) == 0)
return 0;
}
if (p->nprefixes >= PERM_MAX)
return -1;
snprintf(p->prefixes[p->nprefixes++], 128, "%.127s", prefix);
return save(p);
}
int cst_perms_reset(cst_perms_t *p) {
if (!p)
return -1;
p->ntools = p->nprefixes = 0;
return save(p);
}
char *cst_perms_describe(const cst_perms_t *p) {
cst_buf_t b = {0};
cst_buf_puts(&b, "always allowed tools: ");
if (!p || !p->ntools)
cst_buf_puts(&b, "(none)");
for (int i = 0; p && i < p->ntools; i++)
cst_buf_printf(&b, "%s%s", i ? ", " : "", p->tools[i]);
cst_buf_puts(&b, "\nallowed shell prefixes: ");
if (!p || !p->nprefixes)
cst_buf_puts(&b, "(none)");
for (int i = 0; p && i < p->nprefixes; i++)
cst_buf_printf(&b, "%s`%s`", i ? ", " : "", p->prefixes[i]);
cst_buf_puts(&b, "\n");
return cst_buf_steal(&b);
}
/* "git status -s" → "git status"; "make -j8" → "make"; "./run.sh x" →
* "./run.sh". Tools with sub-commands keep two words. */
void cst_perms_suggest_prefix(const char *cmd, char *out, size_t out_sz) {
static const char *const two_word[] = {"git", "npm", "pnpm", "yarn",
"cargo", "go", "docker", "kubectl",
"pip", "python", "python3", "uv",
"bun", "dotnet", "mvn", "gradle"};
/* A prefix rule for these would allow almost anything. */
static const char *const never[] = {
"sudo", "su", "doas", "rm", "dd", "mkfs", "shutdown", "reboot",
"sh", "bash", "zsh", "fish", "dash", "env", "xargs", "eval",
"exec", "nohup", "time", "nice", "timeout", "find", "chmod", "chown",
"curl", "wget", "ssh", "scp", "node", "perl", "ruby", "busybox"};
out[0] = 0;
if (!cmd)
return;
while (*cmd == ' ' || *cmd == '\t')
cmd++;
size_t w1 = strcspn(cmd, " \t");
if (w1 == 0)
return;
for (size_t i = 0; i < sizeof(never) / sizeof(never[0]); i++) {
if (strlen(never[i]) == w1 && strncmp(cmd, never[i], w1) == 0)
return;
}
size_t take = w1;
for (size_t i = 0; i < sizeof(two_word) / sizeof(two_word[0]); i++) {
if (strlen(two_word[i]) == w1 && strncmp(cmd, two_word[i], w1) == 0) {
const char *r = cmd + w1;
while (*r == ' ' || *r == '\t')
r++;
size_t w2 = strcspn(r, " \t");
if (w2 == 0 || r[0] == '-')
return; /* "python3 -c ..." / bare "git": no safe rule */
take = (size_t)(r - cmd) + w2;
break;
}
}
if (take >= out_sz)
take = out_sz - 1;
memcpy(out, cmd, take);
out[take] = 0;
if (has_shell_meta(out))
out[0] = 0;
}
+2
View File
@@ -1,4 +1,6 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#endif
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
+166 -171
View File
@@ -1,10 +1,14 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE #define _DEFAULT_SOURCE
#endif
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE #define _GNU_SOURCE
#endif #endif
#include "cJSON.h"
#include "cst_agent.h" #include "cst_agent.h"
#include <pthread.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -49,6 +53,7 @@ struct cst_session {
int nmsgs; int nmsgs;
int cap; int cap;
FILE *fp; FILE *fp;
pthread_mutex_t mu;
}; };
static char *xstrdup(const char *s) { static char *xstrdup(const char *s) {
@@ -77,6 +82,8 @@ static const char *role_str(cst_role_t r) {
} }
static cst_role_t role_from(const char *s) { static cst_role_t role_from(const char *s) {
if (!s)
return CST_ROLE_USER;
if (strcmp(s, "system") == 0) if (strcmp(s, "system") == 0)
return CST_ROLE_SYSTEM; return CST_ROLE_SYSTEM;
if (strcmp(s, "assistant") == 0) if (strcmp(s, "assistant") == 0)
@@ -86,60 +93,11 @@ static cst_role_t role_from(const char *s) {
return CST_ROLE_USER; return CST_ROLE_USER;
} }
static int json_escape_write(FILE *fp, const char *s) { static void msg_free(cst_message_t *m) {
fputc('"', fp); free(m->content);
for (; s && *s; s++) { free(m->tool_calls_json);
unsigned char c = (unsigned char)*s; free(m->meta_json);
if (c == '"' || c == '\\') { m->content = m->tool_calls_json = m->meta_json = NULL;
fputc('\\', fp);
fputc(c, fp);
} else if (c == '\n') {
fputs("\\n", fp);
} else if (c == '\r') {
fputs("\\r", fp);
} else if (c == '\t') {
fputs("\\t", fp);
} else if (c < 0x20) {
fprintf(fp, "\\u%04x", c);
} else {
fputc(c, fp);
}
}
fputc('"', fp);
return 0;
}
/* Minimal extract "key":"value" from a single JSONL object line. */
static int extract_str(const char *line, const char *key, char *out,
size_t out_sz) {
char pat[64];
snprintf(pat, sizeof(pat), "\"%s\":\"", key);
const char *p = strstr(line, pat);
if (!p)
return -1;
p += strlen(pat);
size_t o = 0;
while (*p && o + 1 < out_sz) {
if (*p == '\\' && p[1]) {
p++;
if (*p == 'n')
out[o++] = '\n';
else if (*p == 'r')
out[o++] = '\r';
else if (*p == 't')
out[o++] = '\t';
else
out[o++] = *p;
p++;
continue;
}
if (*p == '"') {
out[o] = 0;
return 0;
}
out[o++] = *p++;
}
return -1;
} }
static int session_grow(cst_session_t *s) { static int session_grow(cst_session_t *s) {
@@ -154,27 +112,30 @@ static int session_grow(cst_session_t *s) {
return 0; return 0;
} }
static int session_push_mem(cst_session_t *s, cst_role_t role, const char *content, static int session_push_mem(cst_session_t *s, const cst_message_t *src) {
const char *tool_id, const char *tool_name,
const char *tool_calls_json) {
if (session_grow(s) != 0) if (session_grow(s) != 0)
return -1; return -1;
cst_message_t *d = &s->msgs[s->nmsgs]; cst_message_t *d = &s->msgs[s->nmsgs];
memset(d, 0, sizeof(*d)); memset(d, 0, sizeof(*d));
d->role = role; d->role = src->role;
d->content = xstrdup(content ? content : ""); d->content = xstrdup(src->content ? src->content : "");
if (!d->content) if (!d->content)
return -1; return -1;
if (tool_id) snprintf(d->tool_call_id, sizeof(d->tool_call_id), "%s", src->tool_call_id);
snprintf(d->tool_call_id, sizeof(d->tool_call_id), "%s", tool_id); snprintf(d->tool_name, sizeof(d->tool_name), "%s", src->tool_name);
if (tool_name) if (src->tool_calls_json && src->tool_calls_json[0])
snprintf(d->tool_name, sizeof(d->tool_name), "%s", tool_name); d->tool_calls_json = xstrdup(src->tool_calls_json);
if (tool_calls_json && tool_calls_json[0]) if (src->meta_json && src->meta_json[0])
d->tool_calls_json = xstrdup(tool_calls_json); d->meta_json = xstrdup(src->meta_json);
s->nmsgs++; s->nmsgs++;
return 0; return 0;
} }
static const char *jstr(const cJSON *o, const char *key) {
const cJSON *it = cJSON_GetObjectItemCaseSensitive(o, key);
return cJSON_IsString(it) ? it->valuestring : NULL;
}
static void load_existing(cst_session_t *s) { static void load_existing(cst_session_t *s) {
FILE *rp = fopen(s->path, "r"); FILE *rp = fopen(s->path, "r");
if (!rp) if (!rp)
@@ -182,116 +143,142 @@ static void load_existing(cst_session_t *s) {
char *line = NULL; char *line = NULL;
size_t cap = 0; size_t cap = 0;
while (getline(&line, &cap, rp) != -1) { while (getline(&line, &cap, rp) != -1) {
char role[32] = {0}; cJSON *o = cJSON_Parse(line);
char *content = malloc(cap > 8 ? cap : 4096); if (!o || !cJSON_IsObject(o)) {
char tid[CTL_MAX_ID] = {0}; cJSON_Delete(o);
char tname[CTL_MAX_NAME] = {0};
if (!content)
continue;
content[0] = 0;
if (extract_str(line, "role", role, sizeof(role)) != 0) {
free(content);
continue; continue;
} }
/* content may be large — reuse line extract into growing buffer */ const char *role = jstr(o, "role");
free(content); if (!role) {
content = malloc(strlen(line) + 1); cJSON_Delete(o);
if (!content)
continue;
if (extract_str(line, "content", content, strlen(line) + 1) != 0) {
free(content);
continue; continue;
} }
extract_str(line, "tool_call_id", tid, sizeof(tid)); cst_message_t m = {0};
extract_str(line, "tool_name", tname, sizeof(tname)); m.role = role_from(role);
char *tcj = malloc(strlen(line) + 1); m.content = (char *)(jstr(o, "content") ? jstr(o, "content") : "");
const char *tcjp = NULL; const char *tid = jstr(o, "tool_call_id");
if (tcj) { const char *tname = jstr(o, "tool_name");
tcj[0] = 0; if (tid)
if (extract_str(line, "tool_calls_json", tcj, strlen(line) + 1) == 0) snprintf(m.tool_call_id, sizeof(m.tool_call_id), "%s", tid);
tcjp = tcj; if (tname)
} snprintf(m.tool_name, sizeof(m.tool_name), "%s", tname);
session_push_mem(s, role_from(role), content, tid[0] ? tid : NULL, char *tcj = NULL;
tname[0] ? tname : NULL, tcjp); const cJSON *tc = cJSON_GetObjectItemCaseSensitive(o, "tool_calls");
if (cJSON_IsArray(tc))
tcj = cJSON_PrintUnformatted(tc);
else if (jstr(o, "tool_calls_json")) /* legacy string field */
tcj = xstrdup(jstr(o, "tool_calls_json"));
m.tool_calls_json = tcj;
char *meta = NULL;
const cJSON *mo = cJSON_GetObjectItemCaseSensitive(o, "meta");
if (cJSON_IsObject(mo))
meta = cJSON_PrintUnformatted(mo);
m.meta_json = meta;
session_push_mem(s, &m);
free(tcj); free(tcj);
free(content); free(meta);
cJSON_Delete(o);
} }
free(line); free(line);
fclose(rp); fclose(rp);
} }
static void free_all(cst_session_t *s) {
for (int i = 0; i < s->nmsgs; i++)
msg_free(&s->msgs[i]);
s->nmsgs = 0;
}
cst_session_t *cst_session_open(const char *path) { cst_session_t *cst_session_open(const char *path) {
cst_session_t *s = calloc(1, sizeof(*s)); cst_session_t *s = calloc(1, sizeof(*s));
if (!s) if (!s)
return NULL; return NULL;
pthread_mutex_init(&s->mu, NULL);
snprintf(s->path, sizeof(s->path), "%s", path ? path : "session.jsonl"); snprintf(s->path, sizeof(s->path), "%s", path ? path : "session.jsonl");
load_existing(s); load_existing(s);
s->fp = fopen(s->path, "a+"); s->fp = fopen(s->path, "a+");
if (!s->fp) { if (!s->fp || session_grow(s) != 0) {
for (int i = 0; i < s->nmsgs; i++) { if (s->fp)
free(s->msgs[i].content); fclose(s->fp);
free(s->msgs[i].tool_calls_json); free_all(s);
}
free(s->msgs); free(s->msgs);
pthread_mutex_destroy(&s->mu);
free(s); free(s);
return NULL; return NULL;
} }
if (!s->msgs) {
s->cap = 32;
s->msgs = calloc((size_t)s->cap, sizeof(cst_message_t));
if (!s->msgs) {
fclose(s->fp);
free(s);
return NULL;
}
}
return s; return s;
} }
void cst_session_close(cst_session_t *s) { void cst_session_close(cst_session_t *s) {
if (!s) if (!s)
return; return;
for (int i = 0; i < s->nmsgs; i++) { free_all(s);
free(s->msgs[i].content);
free(s->msgs[i].tool_calls_json);
}
free(s->msgs); free(s->msgs);
if (s->fp) if (s->fp)
fclose(s->fp); fclose(s->fp);
pthread_mutex_destroy(&s->mu);
free(s); free(s);
} }
void cst_session_lock(cst_session_t *s) {
if (s)
pthread_mutex_lock(&s->mu);
}
void cst_session_unlock(cst_session_t *s) {
if (s)
pthread_mutex_unlock(&s->mu);
}
static int write_line(FILE *fp, const cst_message_t *msg) {
cJSON *o = cJSON_CreateObject();
if (!o)
return -1;
cJSON_AddStringToObject(o, "role", role_str(msg->role));
cJSON_AddStringToObject(o, "content", msg->content ? msg->content : "");
if (msg->tool_call_id[0])
cJSON_AddStringToObject(o, "tool_call_id", msg->tool_call_id);
if (msg->tool_name[0])
cJSON_AddStringToObject(o, "tool_name", msg->tool_name);
if (msg->tool_calls_json && msg->tool_calls_json[0]) {
cJSON *tc = cJSON_Parse(msg->tool_calls_json);
if (cJSON_IsArray(tc))
cJSON_AddItemToObject(o, "tool_calls", tc);
else {
cJSON_Delete(tc);
cJSON_AddStringToObject(o, "tool_calls_json", msg->tool_calls_json);
}
}
if (msg->meta_json && msg->meta_json[0]) {
cJSON *mo = cJSON_Parse(msg->meta_json);
if (cJSON_IsObject(mo))
cJSON_AddItemToObject(o, "meta", mo);
else
cJSON_Delete(mo);
}
char *txt = cJSON_PrintUnformatted(o);
cJSON_Delete(o);
if (!txt)
return -1;
fputs(txt, fp);
fputc('\n', fp);
free(txt);
fflush(fp);
return 0;
}
int cst_session_append(cst_session_t *s, const cst_message_t *msg) { int cst_session_append(cst_session_t *s, const cst_message_t *msg) {
if (!s || !msg) if (!s || !msg)
return -1; return -1;
if (session_push_mem(s, msg->role, msg->content, msg->tool_call_id, pthread_mutex_lock(&s->mu);
msg->tool_name, msg->tool_calls_json) != 0) int rc = session_push_mem(s, msg);
return -1; if (rc == 0 && s->fp)
rc = write_line(s->fp, msg);
fprintf(s->fp, "{\"role\":"); pthread_mutex_unlock(&s->mu);
json_escape_write(s->fp, role_str(msg->role)); return rc;
fprintf(s->fp, ",\"content\":");
json_escape_write(s->fp, msg->content ? msg->content : "");
if (msg->tool_call_id[0]) {
fprintf(s->fp, ",\"tool_call_id\":");
json_escape_write(s->fp, msg->tool_call_id);
}
if (msg->tool_name[0]) {
fprintf(s->fp, ",\"tool_name\":");
json_escape_write(s->fp, msg->tool_name);
}
if (msg->tool_calls_json && msg->tool_calls_json[0]) {
fprintf(s->fp, ",\"tool_calls_json\":");
json_escape_write(s->fp, msg->tool_calls_json);
}
fprintf(s->fp, "}\n");
fflush(s->fp);
return 0;
} }
int cst_session_count(const cst_session_t *s) { int cst_session_count(const cst_session_t *s) { return s ? s->nmsgs : 0; }
return s ? s->nmsgs : 0;
}
const cst_message_t *cst_session_get(const cst_session_t *s, int i) { const cst_message_t *cst_session_get(const cst_session_t *s, int i) {
if (!s || i < 0 || i >= s->nmsgs) if (!s || i < 0 || i >= s->nmsgs)
@@ -303,9 +290,19 @@ const char *cst_session_path(const cst_session_t *s) {
return s ? s->path : NULL; return s ? s->path : NULL;
} }
static int reopen_truncate(cst_session_t *s) {
if (s->fp) {
fclose(s->fp);
s->fp = NULL;
}
s->fp = fopen(s->path, "w+");
return s->fp ? 0 : -1;
}
int cst_session_clear(cst_session_t *s, int keep_system) { int cst_session_clear(cst_session_t *s, int keep_system) {
if (!s) if (!s)
return -1; return -1;
pthread_mutex_lock(&s->mu);
cst_message_t sys = {0}; cst_message_t sys = {0};
int have_sys = 0; int have_sys = 0;
if (keep_system) { if (keep_system) {
@@ -313,51 +310,49 @@ int cst_session_clear(cst_session_t *s, int keep_system) {
if (s->msgs[i].role == CST_ROLE_SYSTEM) { if (s->msgs[i].role == CST_ROLE_SYSTEM) {
sys.role = CST_ROLE_SYSTEM; sys.role = CST_ROLE_SYSTEM;
sys.content = xstrdup(s->msgs[i].content ? s->msgs[i].content : ""); sys.content = xstrdup(s->msgs[i].content ? s->msgs[i].content : "");
have_sys = 1; have_sys = sys.content != NULL;
break; break;
} }
} }
} }
for (int i = 0; i < s->nmsgs; i++) { free_all(s);
free(s->msgs[i].content); int rc = reopen_truncate(s);
free(s->msgs[i].tool_calls_json); if (rc == 0 && have_sys) {
session_push_mem(s, &sys);
write_line(s->fp, &sys);
} }
s->nmsgs = 0; free(sys.content);
if (s->fp) { pthread_mutex_unlock(&s->mu);
fclose(s->fp); return rc;
s->fp = NULL;
}
s->fp = fopen(s->path, "w+");
if (!s->fp) {
if (have_sys)
free(sys.content);
return -1;
}
if (have_sys) {
cst_session_append(s, &sys);
free(sys.content);
}
return 0;
} }
int cst_session_rewrite(cst_session_t *s, const cst_message_t *msgs, int n) { int cst_session_rewrite(cst_session_t *s, const cst_message_t *msgs, int n) {
if (!s || n < 0) if (!s || n < 0)
return -1; return -1;
for (int i = 0; i < s->nmsgs; i++) { /* msgs may point into this session's own storage: copy before freeing. */
free(s->msgs[i].content); cst_message_t *copy = calloc((size_t)(n ? n : 1), sizeof(*copy));
free(s->msgs[i].tool_calls_json); if (!copy)
}
s->nmsgs = 0;
if (s->fp) {
fclose(s->fp);
s->fp = NULL;
}
s->fp = fopen(s->path, "w+");
if (!s->fp)
return -1; return -1;
for (int i = 0; i < n; i++) { for (int i = 0; i < n; i++) {
if (cst_session_append(s, &msgs[i]) != 0) copy[i] = msgs[i];
return -1; copy[i].content = strdup(msgs[i].content ? msgs[i].content : "");
copy[i].tool_calls_json =
msgs[i].tool_calls_json ? strdup(msgs[i].tool_calls_json) : NULL;
copy[i].meta_json = msgs[i].meta_json ? strdup(msgs[i].meta_json) : NULL;
} }
return 0; pthread_mutex_lock(&s->mu);
free_all(s);
int rc = reopen_truncate(s);
for (int i = 0; rc == 0 && i < n; i++) {
if (session_push_mem(s, &copy[i]) != 0 || write_line(s->fp, &copy[i]) != 0)
rc = -1;
}
pthread_mutex_unlock(&s->mu);
for (int i = 0; i < n; i++) {
free(copy[i].content);
free(copy[i].tool_calls_json);
free(copy[i].meta_json);
}
free(copy);
return rc;
} }
+121
View File
@@ -0,0 +1,121 @@
#include "cst_agent.h"
#include "os_compat.h"
#include "cJSON.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int settings_path(char *out, size_t n) {
char dir[OS_PATH_MAX];
if (os_config_dir(dir, sizeof(dir)) != 0)
return -1;
return os_path_join(out, n, dir, "settings.json");
}
static void copy_str(char *dst, size_t n, const cJSON *v) {
if (cJSON_IsString(v) && v->valuestring)
snprintf(dst, n, "%s", v->valuestring);
}
int cst_settings_load(cst_settings_t *s) {
memset(s, 0, sizeof(*s));
char path[OS_PATH_MAX];
if (settings_path(path, sizeof(path)) != 0)
return -1;
FILE *fp = fopen(path, "rb");
if (!fp)
return -1;
char buf[16384];
size_t n = fread(buf, 1, sizeof(buf) - 1, fp);
fclose(fp);
buf[n] = 0;
cJSON *root = cJSON_Parse(buf);
if (!cJSON_IsObject(root)) {
cJSON_Delete(root);
return -1;
}
copy_str(s->provider, sizeof(s->provider),
cJSON_GetObjectItemCaseSensitive(root, "provider"));
copy_str(s->effort, sizeof(s->effort),
cJSON_GetObjectItemCaseSensitive(root, "effort"));
const cJSON *models = cJSON_GetObjectItemCaseSensitive(root, "models");
const cJSON *m;
cJSON_ArrayForEach(m, models) {
if (s->nmodels >= CST_SETTINGS_MAX_MODELS)
break;
if (!m->string || !cJSON_IsString(m))
continue;
snprintf(s->models[s->nmodels].provider,
sizeof(s->models[s->nmodels].provider), "%s", m->string);
copy_str(s->models[s->nmodels].model, sizeof(s->models[s->nmodels].model),
m);
s->nmodels++;
}
cJSON_Delete(root);
return 0;
}
int cst_settings_save(const cst_settings_t *s) {
char dir[OS_PATH_MAX], path[OS_PATH_MAX], tmp[OS_PATH_MAX + 8];
if (os_config_dir(dir, sizeof(dir)) != 0 || os_mkdir_p(dir, 0700) != 0 ||
settings_path(path, sizeof(path)) != 0)
return -1;
if (snprintf(tmp, sizeof(tmp), "%s.tmp", path) >= (int)sizeof(tmp))
return -1;
cJSON *root = cJSON_CreateObject();
if (s->provider[0])
cJSON_AddStringToObject(root, "provider", s->provider);
if (s->effort[0])
cJSON_AddStringToObject(root, "effort", s->effort);
cJSON *models = cJSON_AddObjectToObject(root, "models");
for (int i = 0; i < s->nmodels; i++)
cJSON_AddStringToObject(models, s->models[i].provider, s->models[i].model);
char *text = cJSON_Print(root);
cJSON_Delete(root);
if (!text)
return -1;
FILE *fp = fopen(tmp, "wb");
int rc = -1;
if (fp) {
int ok = fputs(text, fp) >= 0 && fputc('\n', fp) != EOF;
if (fclose(fp) == 0 && ok) {
#ifdef _WIN32
remove(path);
#endif
rc = rename(tmp, path) == 0 ? 0 : -1;
}
if (rc != 0)
remove(tmp);
}
cJSON_free(text);
return rc;
}
const char *cst_settings_model(const cst_settings_t *s, const char *provider) {
for (int i = 0; provider && i < s->nmodels; i++) {
if (strcmp(s->models[i].provider, provider) == 0 && s->models[i].model[0])
return s->models[i].model;
}
return NULL;
}
int cst_settings_remember(const cst_agent_config_t *cfg) {
cst_settings_t s;
cst_settings_load(&s);
snprintf(s.provider, sizeof(s.provider), "%s", cfg->provider);
snprintf(s.effort, sizeof(s.effort), "%s", cfg->effort);
int i = 0;
while (i < s.nmodels && strcmp(s.models[i].provider, cfg->provider) != 0)
i++;
if (i == s.nmodels) {
if (s.nmodels >= CST_SETTINGS_MAX_MODELS)
return -1;
s.nmodels++;
}
snprintf(s.models[i].provider, sizeof(s.models[i].provider), "%s",
cfg->provider);
snprintf(s.models[i].model, sizeof(s.models[i].model), "%s", cfg->model);
return cst_settings_save(&s);
}
+108
View File
@@ -0,0 +1,108 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cst_tools.h"
#include "cst_util.h"
#include "tools_internal.h"
/* Accepts `[ ] x`, `- [x] x`, `[~] x`, `* [-] x`; bare lines count as todo. */
static int todo_line(const char *s, size_t n, char *mark, const char **text,
size_t *tn) {
while (n && (*s == ' ' || *s == '\t')) {
s++;
n--;
}
while (n && (s[n - 1] == ' ' || s[n - 1] == '\r' || s[n - 1] == '\t'))
n--;
if (n && (*s == '-' || *s == '*') && n > 1 && s[1] == ' ') {
s += 2;
n -= 2;
}
if (!n)
return 0;
*mark = ' ';
if (n >= 3 && s[0] == '[' && s[2] == ']') {
char m = s[1];
if (m == 'x' || m == 'X')
*mark = 'x';
else if (m == '~' || m == '-' || m == '>' || m == '/')
*mark = '~';
s += 3;
n -= 3;
while (n && *s == ' ') {
s++;
n--;
}
}
if (!n)
return 0;
*text = s;
*tn = n;
return 1;
}
int cst_tool_todo_write(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
size_t len = 0;
const char *todos = tool_arg_n(c, "todos", &len);
if (!todos)
return tool_set_result(r, "error: missing todos", 1);
cst_buf_t list = {0};
int total = 0, done = 0, doing = 0;
for (size_t i = 0; i < len;) {
const char *e = memchr(todos + i, '\n', len - i);
size_t end = e ? (size_t)(e - todos) : len;
char mark;
const char *text;
size_t tn;
if (todo_line(todos + i, end - i, &mark, &text, &tn)) {
cst_buf_printf(&list, "[%c] ", mark);
cst_buf_append(&list, text, tn);
cst_buf_puts(&list, "\n");
total++;
done += mark == 'x';
doing += mark == '~';
}
i = end + 1;
}
if (!total) {
cst_buf_free(&list);
return tool_set_result(r, "error: no todo items (one per line: `[ ] task`)",
1);
}
if (ctx->set_todo)
ctx->set_todo(list.buf, ctx->todo_ud);
cst_buf_t b = {0};
cst_buf_printf(&b, "todo list updated: %d/%d done, %d in progress\n", done,
total, doing);
cst_buf_append(&b, list.buf, list.len);
cst_buf_free(&list);
return tool_take_buf(r, &b, 0);
}
int cst_tool_ask_user(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
const char *q = tool_arg(c, "question");
const char *opts = tool_arg(c, "options");
if (!q || !q[0])
return tool_set_result(r, "error: missing question", 1);
if (!ctx->ask)
return tool_set_result(r,
"error: no interactive user is available (headless "
"run). Decide yourself and state the assumption "
"in your answer.",
1);
char ans[2048];
ans[0] = 0;
if (ctx->ask(q, opts, ans, sizeof(ans), ctx->ask_ud) != 0)
return tool_set_result(r,
"the user dismissed the question without "
"answering; continue with your best judgement",
1);
return tool_set_resultf(r, 0, "user answered: %s", ans);
}
+280
View File
@@ -0,0 +1,280 @@
/* Line diff (Myers O(ND)) rendered like GNU `diff -u`. */
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "cst_tools.h"
#include "cst_util.h"
#define DIFF_CONTEXT 3
/* Edit distance beyond this falls back to "replace the changed middle" so
* the trace stays O(DMAX^2) ints. */
#define DIFF_DMAX 2000
typedef struct {
const char *p;
size_t n;
int nl; /* line ended with '\n' */
uint64_t h;
} dline_t;
typedef struct {
char op; /* ' ', '-', '+' */
const dline_t *l;
} dop_t;
static dline_t *split_lines(const char *s, size_t len, int *count) {
int cap = 64, n = 0;
dline_t *v = malloc((size_t)cap * sizeof(*v));
if (!v)
return NULL;
size_t i = 0;
while (i < len) {
const char *nl = memchr(s + i, '\n', len - i);
size_t e = nl ? (size_t)(nl - s) : len;
if (n == cap) {
cap *= 2;
dline_t *nv = realloc(v, (size_t)cap * sizeof(*v));
if (!nv) {
free(v);
return NULL;
}
v = nv;
}
v[n].p = s + i;
v[n].n = e - i;
v[n].nl = nl != NULL;
v[n].h = cst_fnv1a64(CST_FNV_INIT, s + i, e - i) ^ (uint64_t)v[n].nl;
n++;
i = nl ? e + 1 : len;
}
*count = n;
return v;
}
static int line_eq(const dline_t *a, const dline_t *b) {
return a->h == b->h && a->n == b->n && a->nl == b->nl &&
memcmp(a->p, b->p, a->n) == 0;
}
/* Middle section edit script into ops (appended). Returns 0 or -1 (oom). */
static int myers(const dline_t *a, int N, const dline_t *b, int M, dop_t *ops,
int *nops) {
int dmax = N + M;
if (dmax > DIFF_DMAX)
dmax = DIFF_DMAX;
int off = dmax + 1;
int *V = calloc((size_t)(2 * dmax + 3), sizeof(int));
int **trace = calloc((size_t)dmax + 1, sizeof(int *));
if (!V || !trace) {
free(V);
free(trace);
return -1;
}
int found = -1;
for (int d = 0; d <= dmax && found < 0; d++) {
for (int k = -d; k <= d; k += 2) {
int x;
if (k == -d || (k != d && V[off + k - 1] < V[off + k + 1]))
x = V[off + k + 1];
else
x = V[off + k - 1] + 1;
int y = x - k;
while (x < N && y < M && line_eq(&a[x], &b[y])) {
x++;
y++;
}
V[off + k] = x;
if (x >= N && y >= M) {
found = d;
break;
}
}
trace[d] = malloc((size_t)(2 * d + 1) * sizeof(int));
if (!trace[d]) {
found = -2;
break;
}
memcpy(trace[d], V + off - d, (size_t)(2 * d + 1) * sizeof(int));
}
int rc = 0;
if (found < 0) {
/* too different (or oom): delete the old middle, insert the new one */
for (int i = 0; i < N; i++)
ops[(*nops)++] = (dop_t){'-', &a[i]};
for (int j = 0; j < M; j++)
ops[(*nops)++] = (dop_t){'+', &b[j]};
} else {
/* backtrack into a reversed list */
dop_t *rev = malloc((size_t)(N + M + 1) * sizeof(dop_t));
if (!rev) {
rc = -1;
} else {
int nr = 0, x = N, y = M;
for (int d = found; d > 0; d--) {
const int *P = trace[d - 1]; /* index k + (d-1) */
int k = x - y;
int pk;
if (k == -d ||
(k != d && P[k - 1 + (d - 1)] < P[k + 1 + (d - 1)]))
pk = k + 1;
else
pk = k - 1;
int px = P[pk + (d - 1)];
int py = px - pk;
while (x > px && y > py) {
rev[nr++] = (dop_t){' ', &a[x - 1]};
x--;
y--;
}
if (x == px)
rev[nr++] = (dop_t){'+', &b[y - 1]};
else
rev[nr++] = (dop_t){'-', &a[x - 1]};
x = px;
y = py;
}
while (x > 0 && y > 0) {
rev[nr++] = (dop_t){' ', &a[x - 1]};
x--;
y--;
}
for (int i = nr - 1; i >= 0; i--)
ops[(*nops)++] = rev[i];
free(rev);
}
}
for (int d = 0; d <= dmax; d++)
free(trace[d]);
free(trace);
free(V);
return rc;
}
/* Within each run of changes put deletions before insertions (GNU order). */
static void normalize_runs(dop_t *ops, int n, dop_t *tmp) {
int i = 0;
while (i < n) {
if (ops[i].op == ' ') {
i++;
continue;
}
int j = i;
while (j < n && ops[j].op != ' ')
j++;
int t = 0;
for (int k = i; k < j; k++)
if (ops[k].op == '-')
tmp[t++] = ops[k];
for (int k = i; k < j; k++)
if (ops[k].op == '+')
tmp[t++] = ops[k];
memcpy(ops + i, tmp, (size_t)(j - i) * sizeof(dop_t));
i = j;
}
}
static void range(cst_buf_t *b, char sign, int before, int count) {
int start = count == 0 ? before : before + 1;
if (count == 1)
cst_buf_printf(b, "%c%d", sign, start);
else
cst_buf_printf(b, "%c%d,%d", sign, start, count);
}
static void emit_line(cst_buf_t *b, const dop_t *o) {
cst_buf_append(b, &o->op, 1);
cst_buf_append(b, o->l->p, o->l->n);
cst_buf_puts(b, "\n");
if (!o->l->nl)
cst_buf_puts(b, "\\ No newline at end of file\n");
}
char *cst_diff_unified(const char *label, const char *a, size_t alen,
const char *b, size_t blen) {
int na = 0, nb = 0;
dline_t *A = split_lines(a ? a : "", alen, &na);
dline_t *B = split_lines(b ? b : "", blen, &nb);
dop_t *ops = malloc((size_t)(na + nb + 1) * sizeof(dop_t));
dop_t *tmp = malloc((size_t)(na + nb + 1) * sizeof(dop_t));
char *res = NULL;
if (!A || !B || !ops || !tmp)
goto out;
int p = 0;
while (p < na && p < nb && line_eq(&A[p], &B[p]))
p++;
int s = 0;
while (s < na - p && s < nb - p && line_eq(&A[na - 1 - s], &B[nb - 1 - s]))
s++;
int nops = 0;
for (int i = 0; i < p; i++)
ops[nops++] = (dop_t){' ', &A[i]};
if (myers(A + p, na - p - s, B + p, nb - p - s, ops, &nops) != 0)
goto out;
for (int i = na - s; i < na; i++)
ops[nops++] = (dop_t){' ', &A[i]};
normalize_runs(ops, nops, tmp);
cst_buf_t o = {0};
int any = 0;
int old_before = 0, new_before = 0; /* lines consumed before op i */
int i = 0;
while (i < nops) {
if (ops[i].op == ' ') {
old_before++;
new_before++;
i++;
continue;
}
/* change at i: hunk starts DIFF_CONTEXT lines earlier */
int start = i - DIFF_CONTEXT < 0 ? 0 : i - DIFF_CONTEXT;
int back = i - start;
int ob = old_before - back, nbf = new_before - back;
int end = i;
for (;;) {
while (end < nops && ops[end].op != ' ')
end++;
int nx = end;
while (nx < nops && ops[nx].op == ' ')
nx++;
if (nx < nops && nx - end <= 2 * DIFF_CONTEXT) {
end = nx;
continue;
}
end = end + DIFF_CONTEXT > nops ? nops : end + DIFF_CONTEXT;
break;
}
int oc = 0, nc = 0;
for (int k = start; k < end; k++) {
if (ops[k].op != '+')
oc++;
if (ops[k].op != '-')
nc++;
}
if (!any) {
cst_buf_printf(&o, "--- a/%s\n+++ b/%s\n", label, label);
any = 1;
}
cst_buf_puts(&o, "@@ ");
range(&o, '-', ob, oc);
cst_buf_puts(&o, " ");
range(&o, '+', nbf, nc);
cst_buf_puts(&o, " @@\n");
for (int k = start; k < end; k++)
emit_line(&o, &ops[k]);
old_before = ob + oc;
new_before = nbf + nc;
i = end;
}
if (!o.buf)
cst_buf_puts(&o, "");
res = cst_buf_steal(&o);
out:
free(A);
free(B);
free(ops);
free(tmp);
return res;
}
+1178
View File
File diff suppressed because it is too large Load Diff
+317
View File
@@ -0,0 +1,317 @@
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
const char *p;
size_t n;
} line_t;
typedef struct {
line_t *v;
size_t n;
size_t cap;
} lines_t;
static int lines_push(lines_t *L, const char *p, size_t n) {
if (L->n == L->cap) {
size_t nc = L->cap ? L->cap * 2 : 64;
line_t *nv = realloc(L->v, nc * sizeof(*nv));
if (!nv)
return -1;
L->v = nv;
L->cap = nc;
}
L->v[L->n].p = p;
L->v[L->n].n = n;
L->n++;
return 0;
}
/* Split into lines without '\n'. A trailing newline does not add an empty
* final line. */
static int split_lines(const char *s, size_t n, lines_t *out) {
size_t start = 0;
for (size_t i = 0; i < n; i++) {
if (s[i] == '\n') {
if (lines_push(out, s + start, i - start) != 0)
return -1;
start = i + 1;
}
}
if (start < n && lines_push(out, s + start, n - start) != 0)
return -1;
return 0;
}
static size_t rtrim_len(const line_t *l) {
size_t n = l->n;
while (n > 0 && (l->p[n - 1] == ' ' || l->p[n - 1] == '\t' ||
l->p[n - 1] == '\r'))
n--;
return n;
}
static int line_eq(const line_t *a, const line_t *b, int loose) {
if (!loose)
return a->n == b->n && memcmp(a->p, b->p, a->n) == 0;
size_t na = rtrim_len(a), nb = rtrim_len(b);
return na == nb && memcmp(a->p, b->p, na) == 0;
}
static int seq_at(const lines_t *file, size_t pos, const lines_t *seq,
int loose) {
if (pos + seq->n > file->n)
return 0;
for (size_t i = 0; i < seq->n; i++) {
if (!line_eq(&file->v[pos + i], &seq->v[i], loose))
return 0;
}
return 1;
}
/* Find seq in file at or after min_pos. hint<0 means unknown. Returns
* position, -1 when absent, -2 when ambiguous without a hint. */
static long locate(const lines_t *file, size_t min_pos, const lines_t *seq,
long hint, int loose) {
if (seq->n > file->n)
return -1;
size_t last = file->n - seq->n;
if (hint >= 0) {
long best = -1;
long best_d = 0;
for (size_t p = min_pos; p <= last; p++) {
if (!seq_at(file, p, seq, loose))
continue;
long d = (long)p - hint;
if (d < 0)
d = -d;
if (best < 0 || d < best_d) {
best = (long)p;
best_d = d;
}
}
return best;
}
long found = -1;
for (size_t p = min_pos; p <= last; p++) {
if (seq_at(file, p, seq, loose)) {
if (found >= 0)
return -2;
found = (long)p;
}
}
return found;
}
static int parse_hunk_header(const char *p, size_t n, long *a, long *b,
long *c, long *d) {
/* "@@ -a[,b] +c[,d] @@..." */
char tmp[128];
if (n >= sizeof(tmp))
n = sizeof(tmp) - 1;
memcpy(tmp, p, n);
tmp[n] = 0;
*b = *d = 1;
if (sscanf(tmp, "@@ -%ld,%ld +%ld,%ld", a, b, c, d) == 4)
return 0;
*b = *d = 1;
if (sscanf(tmp, "@@ -%ld +%ld,%ld", a, c, d) == 3)
return 0;
*b = *d = 1;
if (sscanf(tmp, "@@ -%ld,%ld +%ld", a, b, c) == 3)
return 0;
*b = *d = 1;
if (sscanf(tmp, "@@ -%ld +%ld", a, c) == 2)
return 0;
return -1;
}
int cst_patch_apply_buffer(const char *orig, size_t olen, const char *hunks,
size_t hlen, char **out, size_t *out_len,
int *nhunks, char *err, size_t err_sz) {
lines_t file = {0}, hl = {0};
lines_t old_seq = {0}, new_seq = {0};
int rc = -1;
int count = 0;
long delta = 0;
size_t min_pos = 0;
int orig_final_nl = olen == 0 || orig[olen - 1] == '\n';
int final_nl = orig_final_nl;
if (err && err_sz)
err[0] = 0;
if (out)
*out = NULL;
if (split_lines(orig ? orig : "", olen, &file) != 0 ||
split_lines(hunks ? hunks : "", hlen, &hl) != 0) {
snprintf(err, err_sz, "oom");
goto done;
}
size_t i = 0;
while (i < hl.n) {
const line_t *h = &hl.v[i];
if (!(h->n >= 2 && h->p[0] == '@' && h->p[1] == '@')) {
i++;
continue;
}
long a = -1, b = -1, c = -1, d = -1;
int have_hint = parse_hunk_header(h->p, h->n, &a, &b, &c, &d) == 0;
i++;
old_seq.n = new_seq.n = 0;
int last_kind = 0; /* ' ', '-', '+' of previous body line */
int new_no_nl = 0, old_no_nl = 0;
for (; i < hl.n; i++) {
const line_t *l = &hl.v[i];
if (l->n >= 2 && l->p[0] == '@' && l->p[1] == '@')
break;
if (l->n == 0) {
/* models often drop the leading space of blank context lines; a
* blank line at the very end of the patch is just a terminator */
if (i + 1 == hl.n)
break;
lines_push(&old_seq, l->p, 0);
lines_push(&new_seq, l->p, 0);
last_kind = ' ';
continue;
}
char tag = l->p[0];
if (tag == ' ') {
lines_push(&old_seq, l->p + 1, l->n - 1);
lines_push(&new_seq, l->p + 1, l->n - 1);
} else if (tag == '-') {
if (l->n >= 4 && memcmp(l->p, "--- ", 4) == 0 && i + 1 < hl.n &&
hl.v[i + 1].n >= 4 && memcmp(hl.v[i + 1].p, "+++ ", 4) == 0)
break; /* next file header */
lines_push(&old_seq, l->p + 1, l->n - 1);
} else if (tag == '+') {
lines_push(&new_seq, l->p + 1, l->n - 1);
} else if (tag == '\\') {
if (last_kind == '+')
new_no_nl = 1;
else if (last_kind == '-')
old_no_nl = 1;
else if (last_kind == ' ')
new_no_nl = old_no_nl = 1;
continue;
} else {
break; /* diff --git, index ..., prose */
}
last_kind = tag;
}
long hint = -1;
if (have_hint) {
/* "-a,0" inserts after line a; otherwise a is 1-based start */
hint = (b == 0 ? a : a - 1) + delta;
if (hint < 0)
hint = 0;
}
long pos;
if (old_seq.n == 0) {
if (hint >= 0)
pos = hint > (long)file.n ? (long)file.n : hint;
else if (file.n == 0)
pos = 0;
else {
snprintf(err, err_sz,
"hunk %d: pure insertion needs @@ -a,0 +c,d @@ line numbers",
count + 1);
goto done;
}
} else {
pos = locate(&file, min_pos, &old_seq, hint, 0);
if (pos == -1)
pos = locate(&file, min_pos, &old_seq, hint, 1);
if (pos == -1 && min_pos > 0) {
pos = locate(&file, 0, &old_seq, hint, 0);
if (pos == -1)
pos = locate(&file, 0, &old_seq, hint, 1);
}
if (pos == -2) {
snprintf(err, err_sz,
"hunk %d: ambiguous, context matches several places; add "
"line numbers or more context",
count + 1);
goto done;
}
if (pos < 0) {
snprintf(err, err_sz,
"hunk %d: context/removed lines not found in file (first: "
"\"%.*s\")",
count + 1, (int)(old_seq.v[0].n > 80 ? 80 : old_seq.v[0].n),
old_seq.v[0].p);
goto done;
}
}
/* splice: file[pos .. pos+old) -> new_seq */
size_t old_n = old_seq.n, new_n = new_seq.n;
size_t need = file.n - old_n + new_n;
if (need > file.cap) {
line_t *nv = realloc(file.v, (need + 16) * sizeof(line_t));
if (!nv) {
snprintf(err, err_sz, "oom");
goto done;
}
file.v = nv;
file.cap = need + 16;
}
memmove(&file.v[pos + new_n], &file.v[pos + old_n],
(file.n - (size_t)pos - old_n) * sizeof(line_t));
for (size_t k = 0; k < new_n; k++)
file.v[pos + k] = new_seq.v[k];
int touches_end = (size_t)pos + new_n == need;
file.n = need;
delta += (long)new_n - (long)old_n;
min_pos = (size_t)pos + new_n;
if (touches_end) {
if (new_no_nl)
final_nl = 0;
else if (old_no_nl)
final_nl = 1;
}
count++;
}
if (count == 0) {
snprintf(err, err_sz, "no @@ hunk found");
goto done;
}
{
size_t total = 1;
for (size_t k = 0; k < file.n; k++)
total += file.v[k].n + 1;
char *buf = malloc(total);
if (!buf) {
snprintf(err, err_sz, "oom");
goto done;
}
size_t o = 0;
for (size_t k = 0; k < file.n; k++) {
memcpy(buf + o, file.v[k].p, file.v[k].n);
o += file.v[k].n;
if (k + 1 < file.n || final_nl)
buf[o++] = '\n';
}
buf[o] = 0;
*out = buf;
if (out_len)
*out_len = o;
}
if (nhunks)
*nhunks = count;
rc = 0;
done:
free(file.v);
free(hl.v);
free(old_seq.v);
free(new_seq.v);
return rc;
}
+342
View File
@@ -0,0 +1,342 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#include "cst_tools.h"
#include "cst_util.h"
#include "tools_internal.h"
static int run_read_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
return cst_tool_read_file2(ctx, c, r);
}
static int run_write_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
return cst_tool_write_file(ctx->workspace_root, c, r);
}
static int run_apply_patch(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
return cst_tool_apply_patch(ctx->workspace_root, c, r);
}
static int run_shell(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
return cst_tool_run_shell(ctx->workspace_root, c, r);
}
#define S "string"
#define I "integer"
#define B "boolean"
static const cst_tool_def_t TOOLS[] = {
{"read_file",
"Read a UTF-8 text file. For big files pass offset (first line, "
"1-based) and limit (number of lines); line_numbers=true prefixes each "
"line with its number.",
"Read a UTF-8 text file under the workspace.",
{{"path", S, 1, "file path relative to the workspace"},
{"offset", I, 0, "first line to return (1-based)"},
{"limit", I, 0, "maximum number of lines"},
{"line_numbers", B, 0, "prefix lines with their numbers"}},
CST_TP_READ, 1, run_read_file, NULL},
{"run_shell",
"Run a non-interactive shell command in the workspace root (stdin is "
"empty; long-running commands are killed). Returns stdout+stderr.",
"Run a shell command in the workspace root (non-interactive, stdin is "
"empty, killed after a timeout). Returns combined stdout+stderr.",
{{"cmd", S, 1, "shell command"}},
CST_TP_SHELL, 1, run_shell, NULL},
{"write_file",
"Create or overwrite a whole file (parent directories are created).",
"Create or overwrite a whole file under the workspace (parent "
"directories are created).",
{{"path", S, 1, "file path"}, {"content", S, 1, "complete new content"}},
CST_TP_WRITE, 1, run_write_file, cst_preview_write_file},
{"apply_patch",
"Apply a unified diff (---/+++ headers, @@ -a,b +c,d @@ hunks with 3 "
"context lines) to one or more files; all hunks apply or nothing "
"changes.",
"Apply a unified diff (---/+++ headers, @@ -a,b +c,d @@ hunks with "
"context lines) to one or more files. All hunks must apply or nothing "
"changes.",
{{"patch", S, 1, "unified diff text"}},
CST_TP_WRITE, 1, run_apply_patch, cst_preview_apply_patch},
{"edit_file",
"Replace an exact snippet in a file. old_string must match the file "
"byte-for-byte (including indentation) and be unique unless "
"replace_all=true. Best for small edits.",
NULL,
{{"path", S, 1, "file path"},
{"old_string", S, 1, "exact text to replace"},
{"new_string", S, 1, "replacement text"},
{"replace_all", B, 0, "replace every occurrence"}},
CST_TP_WRITE, 0, cst_tool_edit_file, cst_preview_edit_file},
{"list_dir",
"List a directory as a tree (directories end with /). Skips .git, "
"node_modules, .agent and build output.",
NULL,
{{"path", S, 0, "directory (default: workspace root)"},
{"depth", I, 0, "levels to descend (default 2, max 6)"}},
CST_TP_READ, 0, cst_tool_list_dir, NULL},
{"glob",
"Find files by name pattern, newest first. `*` and `?` stay within one "
"path segment, `**` spans directories (`src/**/*.c`); a pattern "
"without / matches file names at any depth.",
NULL,
{{"pattern", S, 1, "glob relative to path"},
{"path", S, 0, "directory to search (default: workspace root)"}},
CST_TP_READ, 0, cst_tool_glob, NULL},
{"grep",
"Search file contents with a POSIX extended regex. Returns "
"file:line:text matches.",
NULL,
{{"pattern", S, 1, "extended regular expression"},
{"path", S, 0, "file or directory (default: workspace root)"},
{"glob", S, 0, "only files whose name matches, e.g. *.c"},
{"ignore_case", B, 0, "case-insensitive"},
{"context", I, 0, "lines of context around each match (max 5)"}},
CST_TP_READ, 0, cst_tool_grep, NULL},
{"delete_file",
"Delete a file (not directories).",
NULL,
{{"path", S, 1, "file path"}},
CST_TP_WRITE, 0, cst_tool_delete_file, cst_preview_delete_file},
{"move_file",
"Move or rename a file; fails if the destination exists.",
NULL,
{{"from", S, 1, "existing file"}, {"to", S, 1, "new path"}},
CST_TP_WRITE, 0, cst_tool_move_file, cst_preview_move_file},
{"todo_write",
"Replace your task list for multi-step work. One item per line: "
"`[ ] todo`, `[~] in progress`, `[x] done`.",
NULL,
{{"todos", S, 1, "the whole list, one item per line"}},
CST_TP_META, 0, cst_tool_todo_write, NULL},
{"web_fetch",
"Fetch an http(s) URL and return its text (HTML is reduced to text).",
NULL,
{{"url", S, 1, "http:// or https:// URL"},
{"max_bytes", I, 0, "maximum bytes to return (default 65536)"}},
CST_TP_NET, 0, cst_tool_web_fetch, NULL},
{"ask_user",
"Ask the user a question and wait for the answer. Use only for "
"decisions that are truly the user's.",
NULL,
{{"question", S, 1, "the question"},
{"options", S, 0, "suggested answers, one per line"}},
CST_TP_ASK, 0, cst_tool_ask_user, NULL},
};
#define NTOOLS ((int)(sizeof(TOOLS) / sizeof(TOOLS[0])))
#undef S
#undef I
#undef B
int cst_tool_profile_parse(const char *s, int *out) {
if (!s || !out)
return -1;
if (strcmp(s, "default") == 0 || strcmp(s, "full") == 0) {
*out = CST_TOOLS_DEFAULT;
return 0;
}
if (strcmp(s, "minimal") == 0) {
*out = CST_TOOLS_MINIMAL;
return 0;
}
return -1;
}
const char *cst_tool_profile_name(int p) {
return p == CST_TOOLS_MINIMAL ? "minimal" : "default";
}
int cst_tool_count(void) { return NTOOLS; }
const cst_tool_def_t *cst_tool_at(int i) {
return i >= 0 && i < NTOOLS ? &TOOLS[i] : NULL;
}
int cst_tool_index(const char *name) {
if (!name)
return -1;
for (int i = 0; i < NTOOLS; i++) {
if (strcmp(TOOLS[i].name, name) == 0)
return i;
}
return -1;
}
const cst_tool_def_t *cst_tool_find(const char *name) {
return cst_tool_at(cst_tool_index(name));
}
int cst_tool_known(const char *name) { return cst_tool_find(name) != NULL; }
int cst_tool_is_shell(const char *name) {
const cst_tool_def_t *t = cst_tool_find(name);
return t && t->perm == CST_TP_SHELL;
}
int cst_tool_is_write(const char *name) {
const cst_tool_def_t *t = cst_tool_find(name);
return t && t->perm == CST_TP_WRITE;
}
int cst_tool_active(const cst_agent_config_t *cfg, const cst_tool_def_t *t) {
if (!t)
return 0;
if (!cfg)
return 1;
if (cfg->tool_profile == CST_TOOLS_MINIMAL && !t->minimal)
return 0;
int i = (int)(t - TOOLS);
return !(cfg->tools_off & (1ULL << i));
}
static int is_minimal(const cst_agent_config_t *cfg) {
return cfg && cfg->tool_profile == CST_TOOLS_MINIMAL;
}
void *cst_tools_schema_cfg(const cst_agent_config_t *cfg) {
cJSON *arr = cJSON_CreateArray();
if (!arr)
return NULL;
int legacy = is_minimal(cfg);
for (int i = 0; i < NTOOLS; i++) {
const cst_tool_def_t *t = &TOOLS[i];
if (!cst_tool_active(cfg, t))
continue;
cJSON *tool = cJSON_CreateObject();
cJSON_AddStringToObject(tool, "type", "function");
cJSON *fn = cJSON_AddObjectToObject(tool, "function");
cJSON_AddStringToObject(fn, "name", t->name);
cJSON_AddStringToObject(fn, "description",
legacy && t->min_desc ? t->min_desc : t->desc);
cJSON *params = cJSON_AddObjectToObject(fn, "parameters");
cJSON_AddStringToObject(params, "type", "object");
cJSON *props = cJSON_AddObjectToObject(params, "properties");
cJSON *req = cJSON_AddArrayToObject(params, "required");
for (int k = 0; k < CST_TOOL_MAX_PARAMS && t->params[k].name; k++) {
const cst_tool_param_t *pp = &t->params[k];
if (legacy && !pp->required)
continue;
cJSON *p = cJSON_AddObjectToObject(props, pp->name);
cJSON_AddStringToObject(p, "type", pp->type);
if (!legacy && pp->desc)
cJSON_AddStringToObject(p, "description", pp->desc);
if (pp->required)
cJSON_AddItemToArray(req, cJSON_CreateString(pp->name));
}
cJSON_AddItemToArray(arr, tool);
}
return arr;
}
void *cst_tools_schema(void) { return cst_tools_schema_cfg(NULL); }
char *cst_tools_prompt(const cst_agent_config_t *cfg) {
cst_buf_t b = {0};
cst_buf_puts(&b, "Tools:\n");
for (int i = 0; i < NTOOLS; i++) {
const cst_tool_def_t *t = &TOOLS[i];
if (!cst_tool_active(cfg, t))
continue;
cst_buf_printf(&b, "- %s(", t->name);
for (int k = 0; k < CST_TOOL_MAX_PARAMS && t->params[k].name; k++)
cst_buf_printf(&b, "%s%s%s", k ? ", " : "", t->params[k].name,
t->params[k].required ? "" : "?");
cst_buf_printf(&b, "): %s\n", t->desc);
}
return cst_buf_steal(&b);
}
char *cst_tools_names(const cst_agent_config_t *cfg) {
cst_buf_t b = {0};
int n = 0;
for (int i = 0; i < NTOOLS; i++) {
if (cst_tool_active(cfg, &TOOLS[i]))
cst_buf_printf(&b, "%s%s", n++ ? ", " : "", TOOLS[i].name);
}
if (!b.buf)
cst_buf_puts(&b, "");
return cst_buf_steal(&b);
}
static int parse_int_value(const char *v, long *out) {
while (*v == ' ' || *v == '"' || *v == '\'')
v++;
char *end = NULL;
long x = strtol(v, &end, 10);
if (end == v)
return -1;
while (*end == ' ' || *end == '"' || *end == '\'' || *end == '\n')
end++;
if (*end)
return -1;
*out = x;
return 0;
}
int cst_tool_validate(const cst_tool_def_t *t, const ctl_tool_call_t *call,
char *err, size_t err_sz) {
for (int k = 0; k < CST_TOOL_MAX_PARAMS && t->params[k].name; k++) {
const cst_tool_param_t *p = &t->params[k];
const char *v = tool_arg(call, p->name);
if (!v && strcmp(t->name, "run_shell") == 0)
v = tool_arg(call, "command");
if (!v) {
if (p->required) {
snprintf(err, err_sz, "error: %s needs argument \"%s\"", t->name,
p->name);
return -1;
}
continue;
}
long x;
if (strcmp(p->type, "integer") == 0 && v[0] && parse_int_value(v, &x)) {
snprintf(err, err_sz, "error: %s: argument \"%s\" must be an integer",
t->name, p->name);
return -1;
}
}
return 0;
}
int cst_tool_run(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *call,
cst_tool_result_t *r) {
if (!ctx || !call || !r)
return -1;
memset(r, 0, sizeof(*r));
const cst_tool_def_t *t = cst_tool_find(call->name);
if (!t)
return tool_set_resultf(r, 1, "error: unknown tool: %.64s", call->name);
char err[256];
if (cst_tool_validate(t, call, err, sizeof(err)) != 0)
return tool_set_result(r, err, 1);
return t->run(ctx, call, r);
}
int cst_tool_preview(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *call,
char **out) {
*out = NULL;
const cst_tool_def_t *t = cst_tool_find(call->name);
if (!t || !t->preview)
return -1;
char err[256];
if (cst_tool_validate(t, call, err, sizeof(err)) != 0) {
*out = strdup(err);
return -1;
}
return t->preview(ctx, call, out);
}
int cst_tool_dispatch(const char *workspace_root, const ctl_tool_call_t *call,
cst_tool_result_t *r) {
cst_tool_ctx_t ctx = {0};
ctx.workspace_root = workspace_root;
return cst_tool_run(&ctx, call, r);
}
+544 -635
View File
File diff suppressed because it is too large Load Diff
+63
View File
@@ -0,0 +1,63 @@
#ifndef CST_TOOLS_INTERNAL_H
#define CST_TOOLS_INTERNAL_H
#include "cst_tools.h"
#include "cst_util.h"
#define READ_FILE_MAX (2 * 1024 * 1024)
/* Value of key, or NULL when absent. */
const char *tool_arg(const ctl_tool_call_t *call, const char *key);
const char *tool_arg_n(const ctl_tool_call_t *call, const char *key,
size_t *len);
/* Integer arg, def when absent or empty. */
long tool_arg_long(const ctl_tool_call_t *call, const char *key, long def);
int tool_arg_bool(const ctl_tool_call_t *call, const char *key, int def);
int tool_set_result(cst_tool_result_t *r, const char *s, int code);
int tool_set_resultf(cst_tool_result_t *r, int code, const char *fmt, ...);
int tool_take_buf(cst_tool_result_t *r, cst_buf_t *b, int code);
/* Whole file (NULL when missing, unreadable or larger than max>0). */
char *tool_slurp(const char *full, size_t *out_len, size_t max);
int tool_write_whole(const char *full, const char *data, size_t n);
void tool_ensure_parent_dirs(const char *workspace_root, const char *rel);
void tool_append_diff(cst_buf_t *b, const char *relpath, const char *oldc,
size_t olen, const char *newc, size_t nlen);
int tool_is_binary(const char *s, size_t n);
/* Workspace-relative display form of an absolute resolved path. */
const char *tool_rel(const char *workspace_real, const char *full);
int cst_tool_read_file2(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_list_dir(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_glob(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_grep(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_edit_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_delete_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_move_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_todo_write(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_ask_user(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_tool_web_fetch(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r);
int cst_preview_write_file(const cst_tool_ctx_t *ctx,
const ctl_tool_call_t *c, char **out);
int cst_preview_apply_patch(const cst_tool_ctx_t *ctx,
const ctl_tool_call_t *c, char **out);
int cst_preview_edit_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
char **out);
int cst_preview_delete_file(const cst_tool_ctx_t *ctx,
const ctl_tool_call_t *c, char **out);
int cst_preview_move_file(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
char **out);
#endif
+285
View File
@@ -0,0 +1,285 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <curl/curl.h>
#include "cst_tools.h"
#include "cst_util.h"
#include "tools_internal.h"
#define FETCH_DOWNLOAD_MAX (5 * 1024 * 1024)
#define FETCH_DEFAULT_OUT (64 * 1024)
#define FETCH_MAX_OUT (1024 * 1024)
typedef struct {
cst_buf_t body;
int overflow;
} fetch_t;
static size_t on_body(char *p, size_t sz, size_t n, void *ud) {
fetch_t *f = ud;
size_t len = sz * n;
if (f->body.len + len > FETCH_DOWNLOAD_MAX) {
f->overflow = 1;
return 0; /* abort */
}
cst_buf_append(&f->body, p, len);
return len;
}
static int tag_is(const char *p, const char *name) {
size_t n = strlen(name);
if (strncasecmp(p, name, n) != 0)
return 0;
char c = p[n];
return c == '>' || c == ' ' || c == '/' || c == '\t' || c == '\n' ||
c == '\r';
}
static const char *const BLOCK_TAGS[] = {
"p", "br", "div", "li", "tr", "h1", "h2", "h3", "h4", "h5", "h6",
"section", "article", "header", "footer", "pre", "table", "ul", "ol",
"blockquote", "hr"};
static void put_entity(cst_buf_t *b, const char **pp, const char *end) {
static const struct {
const char *name;
const char *rep;
} ents[] = {{"&amp;", "&"}, {"&lt;", "<"}, {"&gt;", ">"},
{"&quot;", "\""}, {"&#39;", "'"}, {"&apos;", "'"},
{"&nbsp;", " "}};
const char *p = *pp;
for (size_t i = 0; i < sizeof(ents) / sizeof(ents[0]); i++) {
size_t n = strlen(ents[i].name);
if ((size_t)(end - p) >= n && strncmp(p, ents[i].name, n) == 0) {
cst_buf_puts(b, ents[i].rep);
*pp = p + n;
return;
}
}
if (end - p > 3 && p[1] == '#') {
char *e = NULL;
long cp = p[2] == 'x' || p[2] == 'X' ? strtol(p + 3, &e, 16)
: strtol(p + 2, &e, 10);
if (e && *e == ';' && cp > 0 && cp < 0x110000) {
char u[4];
int k = 0;
if (cp < 0x80) {
u[k++] = (char)cp;
} else if (cp < 0x800) {
u[k++] = (char)(0xC0 | (cp >> 6));
u[k++] = (char)(0x80 | (cp & 0x3F));
} else if (cp < 0x10000) {
u[k++] = (char)(0xE0 | (cp >> 12));
u[k++] = (char)(0x80 | ((cp >> 6) & 0x3F));
u[k++] = (char)(0x80 | (cp & 0x3F));
} else {
u[k++] = (char)(0xF0 | (cp >> 18));
u[k++] = (char)(0x80 | ((cp >> 12) & 0x3F));
u[k++] = (char)(0x80 | ((cp >> 6) & 0x3F));
u[k++] = (char)(0x80 | (cp & 0x3F));
}
cst_buf_append(b, u, (size_t)k);
*pp = e + 1;
return;
}
}
cst_buf_append(b, p, 1);
*pp = p + 1;
}
/* Crude HTML → text: drop script/style/head, tags become spaces or
* newlines, entities decoded, blank runs collapsed. */
static char *html_to_text(const char *s, size_t n) {
cst_buf_t b = {0};
const char *p = s, *end = s + n;
for (const char *t = s; t + 7 < end; t++) {
if (*t == '<' && strncasecmp(t + 1, "title", 5) == 0) {
const char *gt = memchr(t, '>', (size_t)(end - t));
const char *e = gt ? gt + 1 : end;
while (e + 7 < end && strncasecmp(e, "</title", 7) != 0)
e++;
if (gt && e + 7 < end && e > gt + 1) {
cst_buf_puts(&b, "# ");
cst_buf_append(&b, gt + 1, (size_t)(e - gt - 1));
cst_buf_puts(&b, "\n\n");
}
break;
}
}
while (p < end) {
if (*p == '<') {
const char *q = p + 1;
int closing = *q == '/';
if (closing)
q++;
if (!closing && (tag_is(q, "script") || tag_is(q, "style") ||
tag_is(q, "head") || tag_is(q, "noscript"))) {
const char *name = tag_is(q, "script") ? "</script"
: tag_is(q, "style") ? "</style"
: tag_is(q, "head") ? "</head"
: "</noscript";
const char *e = p;
size_t nl = strlen(name);
while (e < end && !(end - e >= (long)nl && strncasecmp(e, name, nl) == 0))
e++;
const char *gt = e < end ? memchr(e, '>', (size_t)(end - e)) : NULL;
p = gt ? gt + 1 : end;
continue;
}
if (end - p >= 4 && strncmp(p, "<!--", 4) == 0) {
const char *e = p + 4;
while (e + 2 < end && strncmp(e, "-->", 3) != 0)
e++;
p = e + 3 <= end ? e + 3 : end;
continue;
}
int block = 0;
for (size_t i = 0; i < sizeof(BLOCK_TAGS) / sizeof(BLOCK_TAGS[0]); i++)
if (tag_is(q, BLOCK_TAGS[i]))
block = 1;
const char *gt = memchr(p, '>', (size_t)(end - p));
p = gt ? gt + 1 : end;
cst_buf_puts(&b, block ? "\n" : " ");
continue;
}
if (*p == '&') {
put_entity(&b, &p, end);
continue;
}
cst_buf_append(&b, p, 1);
p++;
}
/* collapse whitespace: spaces within lines, at most one blank line */
cst_buf_t o = {0};
int nls = 0, sp = 0;
for (size_t i = 0; i < b.len; i++) {
char ch = b.buf[i];
if (ch == '\r')
continue;
if (ch == '\n') {
if (nls < 2 && o.len)
cst_buf_puts(&o, "\n");
nls++;
sp = 0;
continue;
}
if (ch == ' ' || ch == '\t') {
sp = 1;
continue;
}
if (sp && nls == 0 && o.len)
cst_buf_puts(&o, " ");
sp = 0;
nls = 0;
cst_buf_append(&o, &ch, 1);
}
cst_buf_free(&b);
if (!o.buf)
cst_buf_puts(&o, "");
return cst_buf_steal(&o);
}
static int textual(const char *ctype) {
if (!ctype || !ctype[0])
return 1;
return strncasecmp(ctype, "text/", 5) == 0 || strstr(ctype, "json") ||
strstr(ctype, "xml") || strstr(ctype, "javascript") ||
strstr(ctype, "yaml") || strstr(ctype, "toml");
}
int cst_tool_web_fetch(const cst_tool_ctx_t *ctx, const ctl_tool_call_t *c,
cst_tool_result_t *r) {
(void)ctx;
const char *url = tool_arg(c, "url");
if (!url || !url[0])
return tool_set_result(r, "error: missing url", 1);
while (*url == ' ')
url++;
if (strncasecmp(url, "http://", 7) != 0 &&
strncasecmp(url, "https://", 8) != 0)
return tool_set_result(r, "error: only http:// and https:// URLs", 1);
long max_out = tool_arg_long(c, "max_bytes", FETCH_DEFAULT_OUT);
if (max_out <= 0)
max_out = FETCH_DEFAULT_OUT;
if (max_out > FETCH_MAX_OUT)
max_out = FETCH_MAX_OUT;
CURL *curl = curl_easy_init();
if (!curl)
return tool_set_result(r, "error: curl init failed", 1);
fetch_t f;
memset(&f, 0, sizeof(f));
char errbuf[CURL_ERROR_SIZE];
errbuf[0] = 0;
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5L);
#if LIBCURL_VERSION_NUM >= 0x075500
curl_easy_setopt(curl, CURLOPT_PROTOCOLS_STR, "http,https");
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "http,https");
#else
curl_easy_setopt(curl, CURLOPT_PROTOCOLS, (long)(CURLPROTO_HTTP | CURLPROTO_HTTPS));
curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS,
(long)(CURLPROTO_HTTP | CURLPROTO_HTTPS));
#endif
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "cst-agent/1.0 (+web_fetch)");
curl_easy_setopt(curl, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, on_body);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &f);
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf);
CURLcode rc = curl_easy_perform(curl);
long http = 0;
char *ctype = NULL;
char *final_url = NULL;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &ctype);
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &final_url);
cst_buf_t b = {0};
int code = 0;
if (rc != CURLE_OK && !f.overflow) {
cst_buf_printf(&b, "error: fetch failed: %s",
errbuf[0] ? errbuf : curl_easy_strerror(rc));
code = 1;
} else {
cst_buf_printf(&b, "HTTP %ld %s\ncontent-type: %s\n\n", http,
final_url ? final_url : url, ctype ? ctype : "?");
if (http >= 400)
code = 1;
if (!textual(ctype)) {
cst_buf_printf(&b, "(non-text content, %zu bytes not shown)\n",
f.body.len);
} else {
char *text = NULL;
size_t tlen = 0;
int html = ctype && strstr(ctype, "html");
if (html) {
text = html_to_text(f.body.buf ? f.body.buf : "", f.body.len);
tlen = text ? strlen(text) : 0;
}
const char *src = html ? text : (f.body.buf ? f.body.buf : "");
size_t slen = html ? tlen : f.body.len;
size_t keep = slen > (size_t)max_out
? cst_utf8_floor(src, (size_t)max_out)
: slen;
size_t clean_len = 0;
char *clean = cst_utf8_sanitize(src, keep, &clean_len);
if (clean)
cst_buf_append(&b, clean, clean_len);
free(clean);
if (keep < slen || f.overflow)
cst_buf_printf(&b, "\n...(truncated; %zu%s bytes in total)\n", slen,
f.overflow ? "+" : "");
free(text);
}
}
curl_easy_cleanup(curl);
cst_buf_free(&f.body);
return tool_take_buf(r, &b, code);
}
+2361 -607
View File
File diff suppressed because it is too large Load Diff
+296
View File
@@ -0,0 +1,296 @@
/* Per-turn file snapshots under <ws>/.agent/undo/<session>/<tag>/ so the
* file changes of the latest turn can be rolled back (/undo). */
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#ifndef _WIN32
#include <dirent.h>
#include <unistd.h>
#endif
#include "cst_agent.h"
#include "cst_undo.h"
#include "cst_util.h"
#include "os_compat.h"
#define UNDO_FILE_MAX (8 * 1024 * 1024)
static int undo_root(const char *ws, const char *sid, char *out, size_t n) {
int k = snprintf(out, n, "%s/.agent/undo/%s", ws, sid && sid[0] ? sid : "_");
return k < 0 || (size_t)k >= n ? -1 : 0;
}
/* Workspace-relative, no "..", not absolute: undo may delete these paths. */
static int rel_ok(const char *rel) {
if (!rel || !rel[0] || rel[0] == '/' || rel[0] == '\\' ||
(rel[0] && rel[1] == ':'))
return 0;
const char *p = rel;
while (*p) {
size_t n = strcspn(p, "/\\");
if (n == 2 && p[0] == '.' && p[1] == '.')
return 0;
p += n;
while (*p == '/' || *p == '\\')
p++;
}
return strchr(rel, '\n') == NULL && strchr(rel, '\t') == NULL;
}
static char *slurp(const char *p, size_t *len, size_t max) {
*len = 0;
FILE *fp = fopen(p, "rb");
if (!fp)
return NULL;
fseek(fp, 0, SEEK_END);
long sz = ftell(fp);
if (sz < 0 || (size_t)sz > max) {
fclose(fp);
return NULL;
}
rewind(fp);
char *b = malloc((size_t)sz + 1);
if (!b) {
fclose(fp);
return NULL;
}
*len = fread(b, 1, (size_t)sz, fp);
b[*len] = 0;
fclose(fp);
return b;
}
static int put(const char *p, const char *d, size_t n) {
FILE *fp = fopen(p, "wb");
if (!fp)
return -1;
int ok = n == 0 || fwrite(d, 1, n, fp) == n;
return fclose(fp) == 0 && ok ? 0 : -1;
}
static int manifest_has(const char *mf, const char *rel) {
FILE *fp = fopen(mf, "r");
if (!fp)
return 0;
char line[1400];
int found = 0;
while (!found && fgets(line, sizeof(line), fp)) {
char *t2 = strchr(line, '\t');
char *t3 = t2 ? strchr(t2 + 1, '\t') : NULL;
if (!t3)
continue;
t3++;
size_t n = strlen(t3);
if (n && t3[n - 1] == '\n')
t3[--n] = 0;
found = strcmp(t3, rel) == 0;
}
fclose(fp);
return found;
}
int cst_undo_snapshot(const char *ws, const char *sid, const char *tag,
const char *rel) {
if (!ws || !tag || !rel_ok(rel))
return -1;
char full[OS_PATH_MAX];
if (cst_sandbox_resolve(ws, rel, full, sizeof(full)) != 0) {
/* parent missing: the file is new; record it as absent */
snprintf(full, sizeof(full), "%s", "");
}
char dir[OS_PATH_MAX], mf[OS_PATH_MAX];
if (undo_root(ws, sid, dir, sizeof(dir)) != 0)
return -1;
size_t dl = strlen(dir);
int k = snprintf(dir + dl, sizeof(dir) - dl, "/%s", tag);
if (k < 0 || (size_t)k >= sizeof(dir) - dl)
return -1;
if (os_mkdir_p(dir, 0755) != 0)
return -1;
k = snprintf(mf, sizeof(mf), "%s/MANIFEST", dir);
if (k < 0 || (size_t)k >= sizeof(mf))
return -1;
if (manifest_has(mf, rel))
return 0;
int idx = 0;
{
FILE *fp = fopen(mf, "r");
if (fp) {
int ch;
while ((ch = fgetc(fp)) != EOF)
idx += ch == '\n';
fclose(fp);
}
}
const char *state = "absent";
struct stat st;
if (full[0] && stat(full, &st) == 0 && S_ISREG(st.st_mode)) {
size_t n = 0;
char *data = slurp(full, &n, UNDO_FILE_MAX);
if (!data) {
state = "toolarge";
} else {
char bak[OS_PATH_MAX + 16];
int bl = snprintf(bak, sizeof(bak), "%s/%d.bak", dir, idx);
state = bl > 0 && (size_t)bl < sizeof(bak) && put(bak, data, n) == 0
? "file"
: "toolarge";
free(data);
}
}
FILE *fp = fopen(mf, "a");
if (!fp)
return -1;
fprintf(fp, "%d\t%s\t%s\n", idx, state, rel);
fclose(fp);
return 0;
}
#ifndef _WIN32
static int latest_tag(const char *root, char *tag, size_t n) {
DIR *d = opendir(root);
if (!d)
return -1;
struct dirent *de;
tag[0] = 0;
while ((de = readdir(d)) != NULL) {
if (de->d_name[0] == '.')
continue;
if (strcmp(de->d_name, tag) > 0)
snprintf(tag, n, "%s", de->d_name);
}
closedir(d);
return tag[0] ? 0 : -1;
}
static void rm_dir(const char *dir) {
DIR *d = opendir(dir);
if (d) {
struct dirent *de;
while ((de = readdir(d)) != NULL) {
if (strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
continue;
char p[OS_PATH_MAX];
if (snprintf(p, sizeof(p), "%s/%s", dir, de->d_name) < (int)sizeof(p))
unlink(p);
}
closedir(d);
}
rmdir(dir);
}
int cst_undo_count(const char *ws, const char *sid) {
char root[OS_PATH_MAX];
if (undo_root(ws, sid, root, sizeof(root)) != 0)
return 0;
DIR *d = opendir(root);
if (!d)
return 0;
int n = 0;
struct dirent *de;
while ((de = readdir(d)) != NULL)
n += de->d_name[0] != '.';
closedir(d);
return n;
}
int cst_undo_restore_last(const char *ws, const char *sid, char *report,
size_t report_sz) {
report[0] = 0;
char root[OS_PATH_MAX], tag[256], dir[OS_PATH_MAX], mf[OS_PATH_MAX];
if (undo_root(ws, sid, root, sizeof(root)) != 0 ||
latest_tag(root, tag, sizeof(tag)) != 0) {
snprintf(report, report_sz, "nothing to undo");
return -1;
}
if (snprintf(dir, sizeof(dir), "%s/%s", root, tag) >= (int)sizeof(dir) ||
snprintf(mf, sizeof(mf), "%s/MANIFEST", dir) >= (int)sizeof(mf)) {
snprintf(report, report_sz, "undo path too long");
return -1;
}
FILE *fp = fopen(mf, "r");
cst_buf_t rep = {0};
int restored = 0, failed = 0;
if (fp) {
char line[1400];
while (fgets(line, sizeof(line), fp)) {
size_t n = strlen(line);
if (n && line[n - 1] == '\n')
line[--n] = 0;
char *t2 = strchr(line, '\t');
char *t3 = t2 ? strchr(t2 + 1, '\t') : NULL;
if (!t3)
continue;
*t2++ = 0;
*t3++ = 0;
const char *rel = t3, *state = t2;
char full[OS_PATH_MAX];
struct stat lst;
if (!rel_ok(rel) || os_path_join(full, sizeof(full), ws, rel) != 0)
continue;
if (lstat(full, &lst) == 0 && S_ISLNK(lst.st_mode)) {
cst_buf_printf(&rep, "skipped %s (now a symlink)\n", rel);
failed++;
continue;
}
if (strcmp(state, "file") == 0) {
char bak[OS_PATH_MAX];
if (snprintf(bak, sizeof(bak), "%s/%s.bak", dir, line) >=
(int)sizeof(bak))
bak[0] = 0;
size_t len = 0;
char *data = slurp(bak, &len, UNDO_FILE_MAX);
char parent[OS_PATH_MAX];
snprintf(parent, sizeof(parent), "%s", full);
char *sl = strrchr(parent, '/');
if (sl) {
*sl = 0;
os_mkdir_p(parent, 0755);
}
if (data && put(full, data, len) == 0) {
cst_buf_printf(&rep, "restored %s\n", rel);
restored++;
} else {
cst_buf_printf(&rep, "FAILED to restore %s\n", rel);
failed++;
}
free(data);
} else if (strcmp(state, "absent") == 0) {
if (unlink(full) == 0) {
cst_buf_printf(&rep, "removed %s (did not exist before)\n", rel);
restored++;
}
} else {
cst_buf_printf(&rep, "skipped %s (too large to snapshot)\n", rel);
failed++;
}
}
fclose(fp);
}
rm_dir(dir);
if (!rep.buf)
cst_buf_puts(&rep, "nothing changed\n");
snprintf(report, report_sz, "%s", rep.buf);
cst_buf_free(&rep);
return failed && !restored ? -1 : 0;
}
#else
int cst_undo_count(const char *ws, const char *sid) {
(void)ws;
(void)sid;
return 0;
}
int cst_undo_restore_last(const char *ws, const char *sid, char *report,
size_t report_sz) {
(void)ws;
(void)sid;
snprintf(report, report_sz, "undo is not available on Windows");
return -1;
}
#endif
+163
View File
@@ -0,0 +1,163 @@
#include "cst_util.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int buf_reserve(cst_buf_t *b, size_t extra) {
if (b->len + extra + 1 <= b->cap)
return 0;
size_t nc = b->cap ? b->cap : 256;
while (nc < b->len + extra + 1)
nc *= 2;
char *nb = realloc(b->buf, nc);
if (!nb)
return -1;
b->buf = nb;
b->cap = nc;
return 0;
}
int cst_buf_append(cst_buf_t *b, const char *s, size_t n) {
if (!b)
return -1;
if (buf_reserve(b, n) != 0)
return -1;
if (n)
memcpy(b->buf + b->len, s, n);
b->len += n;
b->buf[b->len] = 0;
return 0;
}
int cst_buf_puts(cst_buf_t *b, const char *s) {
return cst_buf_append(b, s ? s : "", s ? strlen(s) : 0);
}
int cst_buf_printf(cst_buf_t *b, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
va_list ap2;
va_copy(ap2, ap);
int n = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
if (n < 0 || buf_reserve(b, (size_t)n) != 0) {
va_end(ap2);
return -1;
}
vsnprintf(b->buf + b->len, (size_t)n + 1, fmt, ap2);
va_end(ap2);
b->len += (size_t)n;
return 0;
}
void cst_buf_free(cst_buf_t *b) {
if (!b)
return;
free(b->buf);
b->buf = NULL;
b->len = b->cap = 0;
}
char *cst_buf_steal(cst_buf_t *b) {
if (buf_reserve(b, 0) != 0)
return NULL;
char *s = b->buf;
b->buf = NULL;
b->len = b->cap = 0;
return s;
}
size_t cst_utf8_floor(const char *s, size_t n) {
if (!s || n == 0)
return 0;
size_t i = n;
/* walk back over at most 3 continuation bytes to the lead byte */
size_t back = 0;
while (i > 0 && back < 3 && ((unsigned char)s[i - 1] & 0xC0) == 0x80) {
i--;
back++;
}
if (i == 0)
return n;
unsigned char lead = (unsigned char)s[i - 1];
size_t need = 1;
if (lead >= 0xF0)
need = 4;
else if (lead >= 0xE0)
need = 3;
else if (lead >= 0xC0)
need = 2;
if (need == 1)
return n; /* ASCII lead; any continuation bytes after it are invalid
anyway and get sanitized later */
if (back + 1 >= need)
return n;
return i - 1; /* sequence incomplete: cut before lead byte */
}
static size_t utf8_seq_ok(const unsigned char *p, size_t left) {
unsigned char c = p[0];
size_t need;
unsigned cp;
if (c < 0x80)
return 1;
if ((c & 0xE0) == 0xC0) {
need = 2;
cp = c & 0x1F;
} else if ((c & 0xF0) == 0xE0) {
need = 3;
cp = c & 0x0F;
} else if ((c & 0xF8) == 0xF0) {
need = 4;
cp = c & 0x07;
} else {
return 0;
}
if (left < need)
return 0;
for (size_t i = 1; i < need; i++) {
if ((p[i] & 0xC0) != 0x80)
return 0;
cp = (cp << 6) | (p[i] & 0x3F);
}
if ((need == 2 && cp < 0x80) || (need == 3 && cp < 0x800) ||
(need == 4 && (cp < 0x10000 || cp > 0x10FFFF)) ||
(cp >= 0xD800 && cp <= 0xDFFF))
return 0;
return need;
}
char *cst_utf8_sanitize(const char *s, size_t n, size_t *out_len) {
char *out = malloc(n + 1);
if (!out)
return NULL;
size_t o = 0;
const unsigned char *p = (const unsigned char *)(s ? s : "");
size_t i = 0;
while (i < n) {
size_t k = utf8_seq_ok(p + i, n - i);
if (k == 0) {
out[o++] = '?';
i++;
continue;
}
memcpy(out + o, p + i, k);
o += k;
i += k;
}
out[o] = 0;
if (out_len)
*out_len = o;
return out;
}
uint64_t cst_fnv1a64(uint64_t h, const void *data, size_t n) {
const unsigned char *p = data;
for (size_t i = 0; i < n; i++) {
h ^= p[i];
h *= 1099511628211ULL;
}
return h;
}
+136
View File
@@ -0,0 +1,136 @@
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"I"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"'ll"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" read"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" first"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" then"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" fix"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" and"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" verify"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" read"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" path"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"2"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" ls"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cat"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" Make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"2"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":">/"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"dev"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"/null"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" ||"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" echo"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" NO"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"MA"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"K"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"EF"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"ILE"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"67ff4cc0-8a08-4234-8329-bee4060c7a4b","object":"chat.completion.chunk","created":1791129423,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":513,"completion_tokens":65,"total_tokens":578,"prompt_tokens_details":{"cached_tokens":0},"prompt_cache_hit_tokens":0,"prompt_cache_miss_tokens":513}}
data: [DONE]
+158
View File
@@ -0,0 +1,158 @@
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"F"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"ixing"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" subtraction"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" to"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" addition"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" apply"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_p"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"atch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" patch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" <<"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"EOF"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_P"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"---"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"++"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"@@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" +"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" @@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"EOF"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_P"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"5"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ed4aba25-8ee5-4dc0-9b7d-9b2dcc423e00","object":"chat.completion.chunk","created":1791129424,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":648,"completion_tokens":76,"total_tokens":724,"prompt_tokens_details":{"cached_tokens":512},"prompt_cache_hit_tokens":512,"prompt_cache_miss_tokens":136}}
data: [DONE]
+128
View File
@@ -0,0 +1,128 @@
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"已"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"修复"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"并"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"验证"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"通过"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"。\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"中"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"由"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"改为"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"。\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"成功"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"执行"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"("},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"g"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"cc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"o"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".o"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"退出"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"码"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"0"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"),"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"编译"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"无"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"错误"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"/"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"警告"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"。"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"640f22a2-d6d3-4733-bc37-ce82a3f92a3d","object":"chat.completion.chunk","created":1791129425,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":815,"completion_tokens":61,"total_tokens":876,"prompt_tokens_details":{"cached_tokens":640},"prompt_cache_hit_tokens":640,"prompt_cache_miss_tokens":175}}
data: [DONE]
+60
View File
@@ -0,0 +1,60 @@
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" read"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" path"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"2"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" ls"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"la"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"fd345b5a-6dca-48ea-89cc-a1b46aba4b4c","object":"chat.completion.chunk","created":1791129419,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":533,"completion_tokens":28,"total_tokens":561,"prompt_tokens_details":{"cached_tokens":0},"prompt_cache_hit_tokens":0,"prompt_cache_miss_tokens":533}}
data: [DONE]
+136
View File
@@ -0,0 +1,136 @@
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" apply"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_p"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"atch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" patch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"168"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"---"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"++"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"@@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" +"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" @@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"18bbad75-0a93-488f-a620-68ff5855d5f1","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":758,"completion_tokens":65,"total_tokens":823,"prompt_tokens_details":{"cached_tokens":512},"prompt_cache_hit_tokens":512,"prompt_cache_miss_tokens":246}}
data: [DONE]
+136
View File
@@ -0,0 +1,136 @@
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" apply"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_p"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"atch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" patch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"167"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"---"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"++"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"@@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" +"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" @@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"32be4327-270c-404a-bb3b-e4fb0cf13a9b","object":"chat.completion.chunk","created":1791129420,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":875,"completion_tokens":66,"total_tokens":941,"prompt_tokens_details":{"cached_tokens":640},"prompt_cache_hit_tokens":640,"prompt_cache_miss_tokens":235}}
data: [DONE]
+136
View File
@@ -0,0 +1,136 @@
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" apply"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_p"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"atch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" patch"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"166"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"---"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"++"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"@@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" +"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"1"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" @@"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"0ded657e-8f2f-4d4e-9c13-d5fb666a86a1","object":"chat.completion.chunk","created":1791129421,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":992,"completion_tokens":65,"total_tokens":1057,"prompt_tokens_details":{"cached_tokens":768},"prompt_cache_hit_tokens":768,"prompt_cache_miss_tokens":224}}
data: [DONE]
+88
View File
@@ -0,0 +1,88 @@
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"3"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" write"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" path"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" content"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"30"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"(int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":",int"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"){"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";}\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"call"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"4"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" run"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"_s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"hell"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"arg"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" cmd"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"end"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"ffc819f8-00d7-4384-98ca-d5c42890dd92","object":"chat.completion.chunk","created":1791129422,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":1109,"completion_tokens":42,"total_tokens":1151,"prompt_tokens_details":{"cached_tokens":896},"prompt_cache_hit_tokens":896,"prompt_cache_miss_tokens":213}}
data: [DONE]
+64
View File
@@ -0,0 +1,64 @@
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"I"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"'ll"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" start"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" by"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" reading"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_00_cQI4DUJGUuIKLJCl2r8X7824","type":"function","function":{"name":"read_file","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"path"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"calc"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".c"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_01_AqMzb2tRQvknY0l4tEjo6908","type":"function","function":{"name":"run_shell","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"cmd"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"ls"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b0c53f2a-4b70-42c6-821c-64cc4b932bb7","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":726,"completion_tokens":74,"total_tokens":800,"prompt_tokens_details":{"cached_tokens":512},"prompt_cache_hit_tokens":512,"prompt_cache_miss_tokens":214}}
data: [DONE]
+96
View File
@@ -0,0 +1,96 @@
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"I"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" see"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" bug"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" subtract"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"s"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" Let"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" me"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" check"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" Make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"file"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" too"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" then"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" fix"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" it"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"."},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_00_0EjqefEETmqoUPI8qzLI2461","type":"function","function":{"name":"read_file","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"path"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"Make"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"file"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"id":"call_01_sBsSZn4J0GLps2eUCaYr7475","type":"function","function":{"name":"read_file","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"path"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"out"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":".txt"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":1,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"db73c1db-5194-4de4-8801-841ca742367a","object":"chat.completion.chunk","created":1791129415,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":900,"completion_tokens":89,"total_tokens":989,"prompt_tokens_details":{"cached_tokens":640},"prompt_cache_hit_tokens":640,"prompt_cache_miss_tokens":260}}
data: [DONE]
+118
View File
@@ -0,0 +1,118 @@
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"Now"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" the"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" fix"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_00_TffhUFBFFmXLxjfgOFaM0933","type":"function","function":{"name":"apply_patch","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"patch"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"---"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" a"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"calc"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".c"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"++"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"+"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" b"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"/"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"calc"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".c"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"@@"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" -"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"1"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" +"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"1"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" @@"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\\n"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-int"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" add"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"(int"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" a"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",int"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" b"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"){"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"return"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" a"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"-b"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":";}\\n"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"+"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"int"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" add"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"(int"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" a"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":",int"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" b"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"){"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"return"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" a"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"+b"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":";}\\n"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"b660d9cb-535f-43ab-a0ad-2478603cd9e2","object":"chat.completion.chunk","created":1791129417,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":1117,"completion_tokens":83,"total_tokens":1200,"prompt_tokens_details":{"cached_tokens":896},"prompt_cache_hit_tokens":896,"prompt_cache_miss_tokens":221}}
data: [DONE]
+48
View File
@@ -0,0 +1,48 @@
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"Now"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" verify"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" with"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"id":"call_00_Zy4eH1WRLL3mNeWbEf6f4361","type":"function","function":{"name":"run_shell","arguments":""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"cmd"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":": "}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"make"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" &&"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" cat"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" calc"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":".c"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" &&"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":" ls"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"\""}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"tool_calls":[{"index":0,"function":{"arguments":"}"}}]},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"8d94d47c-9098-4983-bfad-f1c88be1bab2","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"tool_calls"}],"usage":{"prompt_tokens":1294,"completion_tokens":48,"total_tokens":1342,"prompt_tokens_details":{"cached_tokens":1152},"prompt_cache_hit_tokens":1152,"prompt_cache_miss_tokens":142}}
data: [DONE]
+152
View File
@@ -0,0 +1,152 @@
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"role":"assistant","content":""},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"修复"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"完成"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"。\n\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"问题"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"中"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"add"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"返回"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"("},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"实现"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"成了"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"减法"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":")。\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"修改"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"改为"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"return"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" a"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"+b"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":";"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`\n"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"-"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"验证"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":":"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"make"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"成功"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"g"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"cc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".c"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" -"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"o"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".o"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" "},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"无"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"错误"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"/"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"警告"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":","},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"已"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"生成"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":" `"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"calc"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":".o"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"`"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":"。"},"logprobs":null,"finish_reason":null}],"usage":null}
data: {"id":"de00fc60-b7a9-416c-8a40-de4c9bd4bac6","object":"chat.completion.chunk","created":1791129418,"model":"deepseek-flash","system_fingerprint":"aeb56401ca74e127821c4f9126dcb669","choices":[{"index":0,"delta":{"content":""},"logprobs":null,"finish_reason":"stop"}],"usage":{"prompt_tokens":1420,"completion_tokens":73,"total_tokens":1493,"prompt_tokens_details":{"cached_tokens":1280},"prompt_cache_hit_tokens":1280,"prompt_cache_miss_tokens":140}}
data: [DONE]
+196
View File
@@ -0,0 +1,196 @@
#define _POSIX_C_SOURCE 200809L
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
static char *apply(const char *orig, const char *hunks, int *rc, int *nh,
char *err, size_t esz) {
char *out = NULL;
size_t out_len = 0;
*rc = cst_patch_apply_buffer(orig, strlen(orig), hunks, strlen(hunks), &out,
&out_len, nh, err, esz);
return out;
}
static void test_buffer_basic(void) {
char err[256];
int rc, nh;
const char *orig = "a\nb\nc\nd\ne\n";
char *out = apply(orig, "@@ -2,3 +2,3 @@\n b\n-c\n+C\n d\n", &rc, &nh, err,
sizeof(err));
EXPECT(rc == 0 && nh == 1, "single hunk applies");
EXPECT(out && strcmp(out, "a\nb\nC\nd\ne\n") == 0, "single hunk result");
free(out);
/* two hunks, the second located relative to the shift of the first */
out = apply(orig, "@@ -1,2 +1,3 @@\n a\n+a2\n b\n@@ -4,2 +5,2 @@\n d\n-e\n+E\n",
&rc, &nh, err, sizeof(err));
EXPECT(rc == 0 && nh == 2, "two hunks apply");
EXPECT(out && strcmp(out, "a\na2\nb\nc\nd\nE\n") == 0, "two hunk result");
free(out);
/* wrong line numbers: found by searching near the hint */
out = apply(orig, "@@ -40,1 +40,1 @@\n-d\n+D\n", &rc, &nh, err, sizeof(err));
EXPECT(rc == 0, "hint off: searched");
EXPECT(out && strcmp(out, "a\nb\nc\nD\ne\n") == 0, "search result");
free(out);
/* bare @@ without numbers, unique context */
out = apply(orig, "@@\n c\n-d\n+dd\n", &rc, &nh, err, sizeof(err));
EXPECT(rc == 0 && out && strcmp(out, "a\nb\nc\ndd\ne\n") == 0, "bare @@");
free(out);
}
static void test_buffer_edge(void) {
char err[256];
int rc, nh;
/* ambiguous context without a line hint is refused */
char *out = apply("x\ny\nx\ny\n", "@@\n-x\n+z\n", &rc, &nh, err, sizeof(err));
EXPECT(rc != 0, "ambiguous refused");
EXPECT(strstr(err, "ambiguous") != NULL, "ambiguous error text");
free(out);
/* the same hunk with a hint picks the hinted occurrence */
out = apply("x\ny\nx\ny\n", "@@ -3,1 +3,1 @@\n-x\n+z\n", &rc, &nh, err,
sizeof(err));
EXPECT(rc == 0 && out && strcmp(out, "x\ny\nz\ny\n") == 0, "hinted occurrence");
free(out);
/* missing context fails and changes nothing (all-or-nothing) */
out = apply("a\nb\n", "@@ -1,1 +1,1 @@\n-a\n+A\n@@ -9,1 +9,1 @@\n-zzz\n+q\n",
&rc, &nh, err, sizeof(err));
EXPECT(rc != 0 && out == NULL, "second hunk fails -> nothing");
free(out);
/* trailing whitespace drift is tolerated by the loose pass */
out = apply("int x; \nint y;\n", "@@ -1,2 +1,2 @@\n int x;\n-int y;\n+int z;\n",
&rc, &nh, err, sizeof(err));
EXPECT(rc == 0 && out && strstr(out, "int z;"), "rtrim-loose match");
free(out);
/* no final newline preserved / added */
out = apply("a\nb", "@@ -2 +2 @@\n-b\n\\ No newline at end of file\n+B\n"
"\\ No newline at end of file\n",
&rc, &nh, err, sizeof(err));
EXPECT(rc == 0 && out && strcmp(out, "a\nB") == 0, "no final newline kept");
free(out);
/* a blank line inside a hunk is empty context (models drop the space) */
out = apply("a\n\nb\n", "@@ -1,3 +1,3 @@\n a\n\n-b\n+B\n", &rc, &nh, err,
sizeof(err));
EXPECT(rc == 0 && out && strcmp(out, "a\n\nB\n") == 0, "blank context line");
free(out);
/* tabs survive */
out = apply("all:\n\tgcc a.c\n", "@@ -2 +2 @@\n-\tgcc a.c\n+\tgcc -O2 a.c\n",
&rc, &nh, err, sizeof(err));
EXPECT(rc == 0 && out && strcmp(out, "all:\n\tgcc -O2 a.c\n") == 0, "tabs");
free(out);
}
static char g_ws[256];
static void put(const char *rel, const char *content) {
char p[512];
snprintf(p, sizeof(p), "%s/%s", g_ws, rel);
FILE *fp = fopen(p, "wb");
fputs(content, fp);
fclose(fp);
}
static char *get(const char *rel) {
char p[512];
snprintf(p, sizeof(p), "%s/%s", g_ws, rel);
FILE *fp = fopen(p, "rb");
if (!fp)
return NULL;
static char buf[8192];
size_t n = fread(buf, 1, sizeof(buf) - 1, fp);
buf[n] = 0;
fclose(fp);
return buf;
}
static int run_patch(const char *patch, cst_tool_result_t *r) {
ctl_tool_call_t c;
memset(&c, 0, sizeof(c));
snprintf(c.id, sizeof(c.id), "t");
snprintf(c.name, sizeof(c.name), "apply_patch");
snprintf(c.args[0].key, sizeof(c.args[0].key), "patch");
c.args[0].value = (char *)patch;
c.args[0].value_len = strlen(patch);
c.nargs = 1;
memset(r, 0, sizeof(*r));
cst_tool_dispatch(g_ws, &c, r);
return r->exit_code;
}
static void test_tool_multi_file(void) {
put("a.c", "int a = 1;\n");
put("b.c", "int b = 2;\n");
put("gone.c", "x\n");
cst_tool_result_t r;
int ec = run_patch("--- a/a.c\n+++ b/a.c\n@@ -1 +1 @@\n-int a = 1;\n+int a = 10;\n"
"--- a/b.c\n+++ b/b.c\n@@ -1 +1 @@\n-int b = 2;\n+int b = 20;\n"
"--- /dev/null\n+++ b/sub/dir/new.c\n@@ -0,0 +1,2 @@\n+one\n+two\n"
"--- a/gone.c\n+++ /dev/null\n@@ -1 +0,0 @@\n-x\n",
&r);
EXPECT(ec == 0, "multi-file patch ok");
if (ec != 0)
fprintf(stderr, "%.*s\n", (int)r.out_len, r.out ? r.out : "");
cst_tool_result_free(&r);
char *s = get("a.c");
EXPECT(s && strcmp(s, "int a = 10;\n") == 0, "a.c updated");
s = get("b.c");
EXPECT(s && strcmp(s, "int b = 20;\n") == 0, "b.c updated");
s = get("sub/dir/new.c");
EXPECT(s && strcmp(s, "one\ntwo\n") == 0, "new file with parent dirs");
EXPECT(get("gone.c") == NULL, "deleted file");
/* second file fails -> first file untouched */
ec = run_patch("--- a/a.c\n+++ b/a.c\n@@ -1 +1 @@\n-int a = 10;\n+int a = 99;\n"
"--- a/b.c\n+++ b/b.c\n@@ -1 +1 @@\n-no such line\n+q\n",
&r);
EXPECT(ec != 0, "failing multi-file patch reports error");
cst_tool_result_free(&r);
s = get("a.c");
EXPECT(s && strcmp(s, "int a = 10;\n") == 0, "all-or-nothing across files");
/* escaping the workspace is refused */
ec = run_patch("--- /dev/null\n+++ b/../escape.c\n@@ -0,0 +1 @@\n+x\n", &r);
EXPECT(ec != 0, "path escape refused");
cst_tool_result_free(&r);
}
int main(void) {
test_buffer_basic();
test_buffer_edge();
snprintf(g_ws, sizeof(g_ws), "/tmp/cst-patch-test-%d", (int)getpid());
mkdir(g_ws, 0755);
test_tool_multi_file();
char cmd[300];
snprintf(cmd, sizeof(cmd), "rm -rf %s /tmp/escape.c", g_ws);
if (system(cmd) != 0)
fprintf(stderr, "warning: cleanup failed\n");
if (nfail) {
fprintf(stderr, "test_apply_patch: %d failed\n", nfail);
return 1;
}
printf("test_apply_patch: OK\n");
return 0;
}
+146
View File
@@ -0,0 +1,146 @@
#ifndef _DEFAULT_SOURCE
#define _DEFAULT_SOURCE
#endif
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#define CHECK(c) \
do { \
if (!(c)) { \
fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \
exit(1); \
} \
} while (0)
static char g_dir[256];
static void write_file(const char *name, const char *text) {
char path[512];
snprintf(path, sizeof(path), "%s/cst-agent/%s", g_dir, name);
FILE *fp = fopen(path, "w");
CHECK(fp);
fputs(text, fp);
fclose(fp);
}
static void test_creds(void) {
char key[512];
const char *from = NULL;
CHECK(cst_creds_resolve("deepseek", key, sizeof(key), &from) != 0);
/* legacy single-key file follows its api_base */
write_file("credentials", "api_key=sk-legacy\n"
"api_base=https://api.minimax.cn/v1/\n");
CHECK(cst_creds_load("minimax", key, sizeof(key)) == 0);
CHECK(strcmp(key, "sk-legacy") == 0);
CHECK(cst_creds_load("deepseek", key, sizeof(key)) != 0);
write_file("credentials", "api_key=sk-old\n");
CHECK(cst_creds_load("minimax", key, sizeof(key)) == 0);
CHECK(cst_creds_load("openai", key, sizeof(key)) != 0);
CHECK(cst_creds_save("deepseek", "sk-ds-1") == 0);
CHECK(cst_creds_save("openai", "sk-oa") == 0);
CHECK(cst_creds_save("deepseek", "sk-ds-2") == 0);
CHECK(cst_creds_load("deepseek", key, sizeof(key)) == 0);
CHECK(strcmp(key, "sk-ds-2") == 0);
CHECK(cst_creds_load("openai", key, sizeof(key)) == 0);
CHECK(strcmp(key, "sk-oa") == 0);
CHECK(cst_creds_load("minimax", key, sizeof(key)) == 0); /* legacy kept */
CHECK(strcmp(key, "sk-old") == 0);
CHECK(cst_creds_save("deepseek", "bad\nkey") != 0);
char path[512];
snprintf(path, sizeof(path), "%s/cst-agent/credentials", g_dir);
struct stat st;
CHECK(stat(path, &st) == 0);
CHECK((st.st_mode & 0077) == 0);
CHECK(cst_creds_resolve("deepseek", key, sizeof(key), &from) == 0);
CHECK(strcmp(from, "saved") == 0);
setenv("MINIMAX_API_KEY", "sk-env", 1);
unlink(path);
CHECK(cst_creds_resolve("minimax", key, sizeof(key), &from) == 0);
CHECK(strcmp(key, "sk-env") == 0 && strcmp(from, "MINIMAX_API_KEY") == 0);
unsetenv("MINIMAX_API_KEY");
}
static void test_settings(void) {
cst_settings_t s;
CHECK(cst_settings_load(&s) != 0);
CHECK(cst_settings_model(&s, "deepseek") == NULL);
cst_agent_config_t cfg;
memset(&cfg, 0, sizeof(cfg));
snprintf(cfg.provider, sizeof(cfg.provider), "deepseek");
snprintf(cfg.model, sizeof(cfg.model), "deepseek-v4-pro");
snprintf(cfg.effort, sizeof(cfg.effort), "high");
CHECK(cst_settings_remember(&cfg) == 0);
snprintf(cfg.provider, sizeof(cfg.provider), "openai");
snprintf(cfg.model, sizeof(cfg.model), "gpt-5");
snprintf(cfg.effort, sizeof(cfg.effort), "max");
CHECK(cst_settings_remember(&cfg) == 0);
CHECK(cst_settings_load(&s) == 0);
CHECK(strcmp(s.provider, "openai") == 0);
CHECK(strcmp(s.effort, "max") == 0);
CHECK(strcmp(cst_settings_model(&s, "deepseek"), "deepseek-v4-pro") == 0);
CHECK(strcmp(cst_settings_model(&s, "openai"), "gpt-5") == 0);
CHECK(s.nmodels == 2);
write_file("settings.json", "{not json");
CHECK(cst_settings_load(&s) != 0);
CHECK(s.provider[0] == 0);
}
static void test_effort(void) {
CHECK(cst_effort_valid("low") && cst_effort_valid("max"));
CHECK(!cst_effort_valid("ultra") && !cst_effort_valid(NULL));
CHECK(strcmp(cst_effort_wire("deepseek", "deepseek-flash", "max"), "max") ==
0);
CHECK(strcmp(cst_effort_wire("openai", "o3-mini", "max"), "high") == 0);
CHECK(cst_effort_wire("openai", "gpt-4.1-mini", "high") == NULL);
CHECK(cst_effort_wire("minimax", "MiniMax-M2.7", "high") == NULL);
cst_message_t m = {0};
m.role = CST_ROLE_USER;
m.content = "hi";
cst_llm_request_t req = {0};
req.model = "deepseek-flash";
req.msgs = &m;
req.nmsgs = 1;
char *body = cst_llm_build_body(&req);
CHECK(body && !strstr(body, "reasoning_effort"));
free(body);
req.reasoning_effort = "high";
body = cst_llm_build_body(&req);
CHECK(body && strstr(body, "\"reasoning_effort\":\"high\""));
free(body);
}
int main(void) {
snprintf(g_dir, sizeof(g_dir), "/tmp/cst-config-XXXXXX");
CHECK(mkdtemp(g_dir));
setenv("XDG_CONFIG_HOME", g_dir, 1);
unsetenv("DEEPSEEK_API_KEY");
unsetenv("MINIMAX_API_KEY");
unsetenv("OPENAI_API_KEY");
char sub[300];
snprintf(sub, sizeof(sub), "%s/cst-agent", g_dir);
CHECK(mkdir(sub, 0700) == 0);
test_creds();
test_settings();
test_effort();
char cmd[400];
snprintf(cmd, sizeof(cmd), "rm -rf '%s'", g_dir);
CHECK(system(cmd) == 0);
printf("test_config: OK\n");
return 0;
}
+204
View File
@@ -0,0 +1,204 @@
#include "ctl.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Heredoc args: `arg <key> <<TAG` ... a line equal to TAG.
* The body is confirmed only by the terminator line, so no prefix of the
* stream may ever produce CALL_READY. */
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
typedef struct {
int ready;
int nargs;
char keys[CTL_MAX_ARGS][CTL_MAX_KEY];
char *vals[CTL_MAX_ARGS];
size_t lens[CTL_MAX_ARGS];
char text[4096];
size_t text_len;
} cap_t;
static void on_ev(const ctl_event_t *ev, void *ud) {
cap_t *c = ud;
if (ev->type == CTL_EV_TEXT && c->text_len + ev->text_len < sizeof(c->text)) {
memcpy(c->text + c->text_len, ev->text, ev->text_len);
c->text_len += ev->text_len;
}
if (ev->type != CTL_EV_CALL_READY)
return;
c->ready++;
c->nargs = ev->call->nargs;
for (int i = 0; i < ev->call->nargs; i++) {
snprintf(c->keys[i], sizeof(c->keys[i]), "%s", ev->call->args[i].key);
free(c->vals[i]);
c->vals[i] = malloc(ev->call->args[i].value_len + 1);
memcpy(c->vals[i], ev->call->args[i].value, ev->call->args[i].value_len);
c->vals[i][ev->call->args[i].value_len] = 0;
c->lens[i] = ev->call->args[i].value_len;
}
}
static void cap_free(cap_t *c) {
for (int i = 0; i < CTL_MAX_ARGS; i++)
free(c->vals[i]);
memset(c, 0, sizeof(*c));
}
/* feed in `chunk`-byte pieces; finish if asked */
static ctl_status_t run(const char *s, size_t n, size_t chunk, int finish,
cap_t *c, ctl_parser_stats_t *st) {
memset(c, 0, sizeof(*c));
ctl_parser_t *p = ctl_parser_create(on_ev, c);
ctl_status_t rc = CTL_OK;
for (size_t i = 0; i < n && rc == CTL_OK; i += chunk) {
size_t k = n - i < chunk ? n - i : chunk;
rc = ctl_parser_feed(p, s + i, k);
}
if (rc == CTL_OK && finish)
rc = ctl_parser_finish(p);
if (st)
ctl_parser_stats(p, st);
ctl_parser_destroy(p);
return rc;
}
static const char *BODY = "all:\n"
"\tgcc -o app main.c\n"
"\n"
"end\n"
"call x y\n"
"arg path 3\n"
"```\n"
" EOF\n"
"last line\n";
static const char *STREAM = "Writing the Makefile.\n"
"call w1 write_file\n"
"arg path Makefile\n"
"arg content <<EOF\n"
"all:\n"
"\tgcc -o app main.c\n"
"\n"
"end\n"
"call x y\n"
"arg path 3\n"
"```\n"
" EOF\n"
"last line\n"
"EOF\n"
"end\n";
static void test_body_verbatim(void) {
size_t chunks[] = {1, 2, 5, 64, 100000};
for (size_t k = 0; k < sizeof(chunks) / sizeof(chunks[0]); k++) {
cap_t c;
ctl_parser_stats_t st;
ctl_status_t rc = run(STREAM, strlen(STREAM), chunks[k], 1, &c, &st);
EXPECT(rc == CTL_OK, "heredoc stream ok");
EXPECT(c.ready == 1, "one call ready");
EXPECT(c.nargs == 2, "two args");
EXPECT(strcmp(c.keys[0], "path") == 0 && strcmp(c.vals[0], "Makefile") == 0,
"inline path");
EXPECT(strcmp(c.keys[1], "content") == 0, "content key");
EXPECT(c.vals[1] && strcmp(c.vals[1], BODY) == 0,
"body verbatim: tabs, blank lines, end/call/arg/fence lines, "
"indented tag");
EXPECT(st.args_heredoc == 1 && st.args_inline == 1, "stats per arg form");
EXPECT(c.text_len == strlen("Writing the Makefile.\n"), "prose is text");
cap_free(&c);
}
}
static void test_never_premature(void) {
size_t n = strlen(STREAM);
int premature = 0;
for (size_t cut = 0; cut < n; cut++) {
cap_t c;
run(STREAM, cut, 7, 0, &c, NULL);
if (c.ready)
premature++;
cap_free(&c);
}
EXPECT(premature == 0, "no prefix of a heredoc call is executable");
/* EOF inside the body: error, nothing executed */
const char *mid = strstr(STREAM, "\tgcc");
cap_t c;
ctl_parser_stats_t st;
ctl_status_t rc = run(STREAM, (size_t)(mid - STREAM), 3, 1, &c, &st);
EXPECT(rc == CTL_ERR_CONTRACT && c.ready == 0, "EOF in heredoc rejected");
EXPECT(st.error_kind == CTL_EK_HEREDOC_EOF, "error kind heredoc_eof");
cap_free(&c);
}
static void test_tag_forms(void) {
const char *quoted = "call q1 write_file\narg path a.txt\n"
"arg content <<'BODY_1'\nx\nBODY_1\nend\n";
cap_t c;
EXPECT(run(quoted, strlen(quoted), 1, 1, &c, NULL) == CTL_OK && c.ready == 1,
"single-quoted tag");
EXPECT(c.vals[1] && strcmp(c.vals[1], "x\n") == 0, "quoted tag body");
cap_free(&c);
const char *dq = "call q2 write_file\narg path a.txt\n"
"arg content << \"T\"\n\nT\nend\n";
EXPECT(run(dq, strlen(dq), 4, 1, &c, NULL) == CTL_OK && c.ready == 1,
"double-quoted tag with space");
EXPECT(c.vals[1] && strcmp(c.vals[1], "\n") == 0, "single blank line body");
cap_free(&c);
const char *empty = "call q3 write_file\narg path a.txt\n"
"arg content <<E\nE\nend\n";
EXPECT(run(empty, strlen(empty), 3, 1, &c, NULL) == CTL_OK && c.ready == 1 &&
c.lens[1] == 0,
"empty body");
cap_free(&c);
const char *bad = "call q4 write_file\narg content <<a-b\nx\na-b\nend\n";
ctl_parser_stats_t st;
EXPECT(run(bad, strlen(bad), 1, 1, &c, &st) == CTL_ERR_CONTRACT &&
st.error_kind == CTL_EK_BAD_ARG && c.ready == 0,
"invalid tag rejected");
cap_free(&c);
/* terminator on the last line without newline, implicit end at EOF */
const char *eof = "call q5 write_file\narg path a.txt\narg content <<EOF\n"
"body\nEOF";
EXPECT(run(eof, strlen(eof), 2, 1, &c, NULL) == CTL_OK && c.ready == 1,
"tag at EOF accepted");
EXPECT(c.vals[1] && strcmp(c.vals[1], "body\n") == 0, "tag at EOF body");
cap_free(&c);
/* mixing all three arg forms in one call */
const char *mix = "call m1 apply_patch\narg note hello world\narg patch 4\n"
"ab\ncarg extra <<X\nq\nX\nend\n";
EXPECT(run(mix, strlen(mix), 1, 1, &c, &st) == CTL_OK && c.ready == 1 &&
c.nargs == 3,
"inline + nbytes + heredoc in one call");
EXPECT(c.vals[1] && strcmp(c.vals[1], "ab\nc") == 0, "nbytes body exact");
EXPECT(st.args_inline == 1 && st.args_nbytes == 1 && st.args_heredoc == 1,
"stats count each form");
cap_free(&c);
}
int main(void) {
test_body_verbatim();
test_never_premature();
test_tag_forms();
if (nfail) {
fprintf(stderr, "test_ctl_heredoc: %d failed\n", nfail);
return 1;
}
printf("test_ctl_heredoc: OK\n");
return 0;
}
+229
View File
@@ -0,0 +1,229 @@
/* Built-in unified diff: GNU `diff -u` layout on unambiguous cases, and a
* randomized round trip through our own patcher. */
#define _POSIX_C_SOURCE 200809L
#include "cst_agent.h"
#include "cst_tools.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
static void put(const char *p, const char *s) {
FILE *fp = fopen(p, "wb");
if (fp) {
fputs(s, fp);
fclose(fp);
}
}
/* GNU diff output, or NULL when diff is not installed. */
static char *gnu_diff(const char *a, const char *b) {
char pa[] = "/tmp/cstdiffaXXXXXX", pb[] = "/tmp/cstdiffbXXXXXX";
int fa = mkstemp(pa), fb = mkstemp(pb);
if (fa < 0 || fb < 0)
return NULL;
close(fa);
close(fb);
put(pa, a);
put(pb, b);
char cmd[256];
snprintf(cmd, sizeof(cmd),
"LC_ALL=C diff -u --label a/f --label b/f %s %s 2>/dev/null", pa,
pb);
FILE *pp = popen(cmd, "r");
char *out = NULL;
if (pp) {
size_t cap = 4096, n = 0;
out = malloc(cap);
int ch;
while (out && (ch = fgetc(pp)) != EOF) {
if (n + 1 >= cap) {
cap *= 2;
out = realloc(out, cap);
}
if (out)
out[n++] = (char)ch;
}
int st = pclose(pp);
if (out)
out[n] = 0;
if (st == -1 || (WEXITSTATUS(st) != 0 && WEXITSTATUS(st) != 1)) {
free(out);
out = NULL;
}
}
unlink(pa);
unlink(pb);
return out;
}
static int have_gnu_diff(void) {
char *o = gnu_diff("x\n", "y\n");
int ok = o && strstr(o, "@@ -1 +1 @@");
free(o);
return ok;
}
static char *mine(const char *a, const char *b) {
return cst_diff_unified("f", a, strlen(a), b, strlen(b));
}
static void same_as_gnu(const char *name, const char *a, const char *b,
int gnu) {
char *m = mine(a, b);
EXPECT(m != NULL, name);
if (gnu && m) {
char *g = gnu_diff(a, b);
if (g && strcmp(g, m) != 0) {
fprintf(stderr, "FAIL %s: differs from diff -u\n--- ours\n%s--- gnu\n%s",
name, m, g);
nfail++;
}
free(g);
}
free(m);
}
/* Apply our diff with our patcher; must reproduce b. */
static int round_trip(const char *a, const char *b) {
char *d = mine(a, b);
if (!d)
return 0;
int ok;
if (!d[0]) {
ok = strcmp(a, b) == 0;
} else {
const char *h = strstr(d, "\n+++ ");
h = h ? strchr(h + 1, '\n') : NULL;
char *out = NULL;
size_t out_len = 0;
int nh = 0;
char err[256];
int rc = h ? cst_patch_apply_buffer(a, strlen(a), h + 1, strlen(h + 1),
&out, &out_len, &nh, err, sizeof(err))
: -1;
ok = rc == 0 && out && out_len == strlen(b) && memcmp(out, b, out_len) == 0;
if (!ok)
fprintf(stderr, "round trip failed (%s)\n--- a\n%s--- b\n%s--- diff\n%s",
rc ? err : "content", a, b, d);
free(out);
}
free(d);
return ok;
}
static char *random_text(unsigned *seed, int lines) {
static const char *const words[] = {"alpha", "beta", "gamma", "delta",
"{", "}", "", "return 0;"};
size_t cap = (size_t)lines * 16 + 2, n = 0;
char *s = malloc(cap);
for (int i = 0; i < lines; i++) {
const char *w = words[rand_r(seed) % 8];
n += (size_t)snprintf(s + n, cap - n, "%s%d\n", w, rand_r(seed) % 3);
}
s[n] = 0;
if (n && rand_r(seed) % 5 == 0)
s[n - 1] = 0; /* no newline at end of file */
return s;
}
static char *mutate(unsigned *seed, const char *a) {
size_t n = strlen(a), cap = n * 2 + 256, o = 0;
char *b = malloc(cap);
const char *p = a;
while (*p) {
const char *e = strchr(p, '\n');
size_t len = e ? (size_t)(e - p) + 1 : strlen(p);
int r = rand_r(seed) % 10;
if (r == 0) {
/* drop the line */
} else if (r == 1) {
o += (size_t)snprintf(b + o, cap - o, "inserted%d\n", rand_r(seed) % 9);
memcpy(b + o, p, len);
o += len;
} else if (r == 2) {
o += (size_t)snprintf(b + o, cap - o, "changed%d\n", rand_r(seed) % 9);
} else {
memcpy(b + o, p, len);
o += len;
}
p += len;
}
b[o] = 0;
return b;
}
int main(void) {
char *m = mine("same\n", "same\n");
EXPECT(m && m[0] == 0, "identical -> empty");
free(m);
int gnu = have_gnu_diff();
if (!gnu)
fprintf(stderr, "note: GNU diff not found; layout checks use round trip "
"only\n");
same_as_gnu("modify", "a\nb\nc\nd\ne\n", "a\nb\nC\nd\ne\n", gnu);
same_as_gnu("insert head", "a\nb\n", "z\na\nb\n", gnu);
same_as_gnu("append", "a\nb\n", "a\nb\nc\n", gnu);
same_as_gnu("delete", "a\nb\nc\n", "a\nc\n", gnu);
same_as_gnu("new file", "", "x\ny\n", gnu);
same_as_gnu("emptied", "x\ny\n", "", gnu);
same_as_gnu("no eol added", "a\nb", "a\nb\n", gnu);
same_as_gnu("no eol kept", "a\nb", "a\nB", gnu);
same_as_gnu("two hunks",
"1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n",
"1\nTWO\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\nFOURTEEN\n15\n",
gnu);
same_as_gnu("merged hunks", "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n",
"1\nX\n3\n4\n5\n6\n7\nY\n9\n10\n", gnu);
m = mine("a\n", "b\n");
EXPECT(m && strncmp(m, "--- a/f\n+++ b/f\n@@ -1 +1 @@\n-a\n+b\n", 34) == 0,
"header layout");
free(m);
unsigned seed = 12345;
int ok = 0, total = 400;
for (int i = 0; i < total; i++) {
char *a = random_text(&seed, 1 + (int)(rand_r(&seed) % 60));
char *b = mutate(&seed, a);
ok += round_trip(a, b);
free(a);
free(b);
}
EXPECT(ok == total, "random round trips");
/* very different large inputs hit the fallback and still round trip */
{
size_t n = 6000;
char *a = malloc(n * 8), *b = malloc(n * 8);
size_t oa = 0, ob = 0;
for (size_t i = 0; i < n; i++) {
oa += (size_t)sprintf(a + oa, "a%zu\n", i);
ob += (size_t)sprintf(b + ob, "b%zu\n", i);
}
EXPECT(round_trip(a, b), "fallback round trip");
free(a);
free(b);
}
if (nfail) {
fprintf(stderr, "test_diff: %d failure(s)\n", nfail);
return 1;
}
printf("test_diff: OK (%d random round trips)\n", total);
return 0;
}
+174
View File
@@ -0,0 +1,174 @@
#include "cJSON.h"
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
static cst_message_t msg(cst_role_t role, const char *content) {
cst_message_t m;
memset(&m, 0, sizeof(m));
m.role = role;
m.content = (char *)content;
return m;
}
static cJSON *build(const cst_message_t *msgs, int n, int tools,
size_t tool_cap) {
cst_llm_request_t r;
memset(&r, 0, sizeof(r));
r.model = "test-model";
r.msgs = msgs;
r.nmsgs = n;
r.enable_tools = tools;
r.tool_out_cap = tool_cap;
char *body = cst_llm_build_body(&r);
EXPECT(body != NULL, "body built");
cJSON *j = body ? cJSON_Parse(body) : NULL;
EXPECT(j != NULL, "body is valid JSON");
free(body);
return j;
}
static const char *content_at(const cJSON *root, int i) {
const cJSON *msgs = cJSON_GetObjectItemCaseSensitive(root, "messages");
const cJSON *m = cJSON_GetArrayItem(msgs, i);
const cJSON *c = cJSON_GetObjectItemCaseSensitive(m, "content");
return cJSON_IsString(c) ? c->valuestring : NULL;
}
static const char *role_at(const cJSON *root, int i) {
const cJSON *msgs = cJSON_GetObjectItemCaseSensitive(root, "messages");
const cJSON *m = cJSON_GetArrayItem(msgs, i);
const cJSON *r = cJSON_GetObjectItemCaseSensitive(m, "role");
return cJSON_IsString(r) ? r->valuestring : "";
}
static int nmsgs(const cJSON *root) {
return cJSON_GetArraySize(cJSON_GetObjectItemCaseSensitive(root, "messages"));
}
/* TAB / quotes / backslash / control bytes / CJK must survive verbatim
* (the old hand-rolled escaper turned a Makefile TAB into HTTP 400). */
static void test_escaping(void) {
const char *text = "all:\n\tgcc -o \"a b\" x.c \\\n\x01\x1f end 中文";
cst_message_t m[2] = {msg(CST_ROLE_SYSTEM, "sys"), msg(CST_ROLE_USER, text)};
cJSON *j = build(m, 2, 0, 0);
const char *c = content_at(j, 1);
EXPECT(c && strcmp(c, text) == 0, "user content roundtrips exactly");
EXPECT(!cJSON_GetObjectItemCaseSensitive(j, "tools"), "no tools when off");
const cJSON *so = cJSON_GetObjectItemCaseSensitive(j, "stream_options");
EXPECT(cJSON_IsTrue(cJSON_GetObjectItemCaseSensitive(so, "include_usage")),
"stream_options.include_usage");
cJSON_Delete(j);
}
static void test_invalid_utf8(void) {
const char text[] = "ok \xff\xfe bad \xe4\xb8 trunc";
cst_message_t m[1] = {msg(CST_ROLE_USER, text)};
cJSON *j = build(m, 1, 0, 0);
const char *c = content_at(j, 0);
EXPECT(c != NULL, "content present");
EXPECT(c && strlen(c) == strlen(text), "sanitize keeps byte length");
EXPECT(c && strstr(c, "ok ") == c, "valid prefix kept");
cJSON_Delete(j);
}
static void test_tool_calls_and_null_content(void) {
cst_message_t m[3];
m[0] = msg(CST_ROLE_USER, "go");
m[1] = msg(CST_ROLE_ASSISTANT, "");
m[1].tool_calls_json =
"[{\"id\":\"c1\",\"type\":\"function\",\"function\":{\"name\":"
"\"read_file\",\"arguments\":\"{\\\"path\\\":\\\"a\\tb.c\\\"}\"}}]";
m[2] = msg(CST_ROLE_TOOL, "[tool read_file id=c1 exit=0]\nhello");
snprintf(m[2].tool_call_id, sizeof(m[2].tool_call_id), "c1");
cJSON *j = build(m, 3, 1, 0);
const cJSON *msgs = cJSON_GetObjectItemCaseSensitive(j, "messages");
const cJSON *a = cJSON_GetArrayItem(msgs, 1);
EXPECT(cJSON_IsNull(cJSON_GetObjectItemCaseSensitive(a, "content")),
"assistant content null with tool_calls");
const cJSON *tc = cJSON_GetObjectItemCaseSensitive(a, "tool_calls");
EXPECT(cJSON_IsArray(tc) && cJSON_GetArraySize(tc) == 1, "tool_calls kept");
const cJSON *t = cJSON_GetArrayItem(msgs, 2);
EXPECT(strcmp(role_at(j, 2), "tool") == 0, "tool role");
const cJSON *tid = cJSON_GetObjectItemCaseSensitive(t, "tool_call_id");
EXPECT(cJSON_IsString(tid) && strcmp(tid->valuestring, "c1") == 0,
"tool_call_id");
const cJSON *tools = cJSON_GetObjectItemCaseSensitive(j, "tools");
EXPECT(cJSON_IsArray(tools) && cJSON_GetArraySize(tools) >= 4,
"tools[] schema present");
const cJSON *choice = cJSON_GetObjectItemCaseSensitive(j, "tool_choice");
EXPECT(cJSON_IsString(choice) && strcmp(choice->valuestring, "auto") == 0,
"tool_choice auto");
cJSON_Delete(j);
}
static void test_merge_and_orphan_tool(void) {
cst_message_t m[3];
m[0] = msg(CST_ROLE_USER, "first");
m[1] = msg(CST_ROLE_TOOL, "orphan output"); /* no id -> user text */
m[2] = msg(CST_ROLE_USER, "third");
cJSON *j = build(m, 3, 0, 0);
EXPECT(nmsgs(j) == 1, "consecutive user messages merged");
const char *c = content_at(j, 0);
EXPECT(c && strstr(c, "first") && strstr(c, "orphan output") &&
strstr(c, "third"),
"merged content keeps all parts in order");
cJSON_Delete(j);
}
static void test_tool_cap_head_tail(void) {
size_t n = 100000;
char *big = malloc(n + 1);
memset(big, 'x', n);
memcpy(big, "HEAD", 4);
memcpy(big + n - 4, "TAIL", 4);
big[n] = 0;
cst_message_t m[3];
m[0] = msg(CST_ROLE_USER, "go");
m[1] = msg(CST_ROLE_ASSISTANT, "");
m[1].tool_calls_json = "[{\"id\":\"c1\",\"type\":\"function\",\"function\":"
"{\"name\":\"run_shell\",\"arguments\":\"{}\"}}]";
m[2] = msg(CST_ROLE_TOOL, big);
snprintf(m[2].tool_call_id, sizeof(m[2].tool_call_id), "c1");
cJSON *j = build(m, 3, 1, 1000);
const char *c = content_at(j, 2);
EXPECT(c && strlen(c) < 1200, "tool output capped");
EXPECT(c && strncmp(c, "HEAD", 4) == 0, "head kept");
EXPECT(c && strcmp(c + strlen(c) - 4, "TAIL") == 0, "tail kept");
EXPECT(c && strstr(c, "omitted"), "omission note");
cJSON_Delete(j);
/* user text (e.g. text-protocol result frames) is never capped */
cst_message_t u[1] = {msg(CST_ROLE_USER, big)};
j = build(u, 1, 0, 1000);
c = content_at(j, 0);
EXPECT(c && strlen(c) == n, "user text not capped");
cJSON_Delete(j);
free(big);
}
int main(void) {
test_escaping();
test_invalid_utf8();
test_tool_calls_and_null_content();
test_merge_and_orphan_tool();
test_tool_cap_head_tail();
if (nfail) {
fprintf(stderr, "test_llm_body: %d failed\n", nfail);
return 1;
}
printf("test_llm_body: OK\n");
return 0;
}
+114
View File
@@ -0,0 +1,114 @@
/* Tool registry: schema / prompt generation, profiles and /tools switches. */
#include "cJSON.h"
#include "cst_agent.h"
#include "cst_tools.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
static int schema_has(cJSON *arr, const char *name) {
const cJSON *t = NULL;
cJSON_ArrayForEach(t, arr) {
const cJSON *fn = cJSON_GetObjectItemCaseSensitive(t, "function");
const cJSON *nm = cJSON_GetObjectItemCaseSensitive(fn, "name");
if (cJSON_IsString(nm) && strcmp(nm->valuestring, name) == 0)
return 1;
}
return 0;
}
int main(void) {
cst_agent_config_t cfg;
memset(&cfg, 0, sizeof(cfg));
/* every tool has a runner and a well-formed parameter list */
EXPECT(cst_tool_count() >= 13, "registry size");
for (int i = 0; i < cst_tool_count(); i++) {
const cst_tool_def_t *t = cst_tool_at(i);
EXPECT(t->run != NULL, t->name);
EXPECT((t->perm == CST_TP_WRITE) == (t->preview != NULL),
"write tools (and only those) have previews");
EXPECT(cst_tool_find(t->name) == t, "find by name");
}
cJSON *all = cst_tools_schema_cfg(&cfg);
EXPECT(cJSON_GetArraySize(all) == cst_tool_count(), "default: every tool");
EXPECT(schema_has(all, "edit_file") && schema_has(all, "grep") &&
schema_has(all, "web_fetch"),
"default schema has new tools");
cJSON_Delete(all);
cfg.tool_profile = CST_TOOLS_MINIMAL;
cJSON *min = cst_tools_schema_cfg(&cfg);
EXPECT(cJSON_GetArraySize(min) == 4, "minimal: four tools");
EXPECT(schema_has(min, "read_file") && schema_has(min, "run_shell") &&
schema_has(min, "write_file") && schema_has(min, "apply_patch"),
"minimal: the original four");
char *ms = cJSON_PrintUnformatted(min);
EXPECT(ms && strstr(ms, "\"description\":\"Read a UTF-8 text file under the "
"workspace.\""),
"minimal keeps the original wording");
EXPECT(ms && !strstr(ms, "offset"), "minimal hides optional params");
free(ms);
cJSON_Delete(min);
char *pmin = cst_agent_build_prompt(&cfg, CST_PROTO_NATIVE_EOS, NULL);
EXPECT(pmin && strcmp(pmin, cst_agent_system_prompt_for(
CST_PROTO_NATIVE_EOS)) == 0,
"minimal prompt == legacy prompt");
EXPECT(pmin && strstr(pmin, "ls/grep via run_shell"), "legacy workflow");
free(pmin);
char *pctl = cst_agent_build_prompt(&cfg, CST_PROTO_CTL, NULL);
EXPECT(pctl && !strstr(pctl, "must be quoted"), "minimal ctl: no new rule");
free(pctl);
cfg.tool_profile = CST_TOOLS_DEFAULT;
char *pdef = cst_agent_build_prompt(&cfg, CST_PROTO_CTL_HEREDOC,
"Always run `make test`.");
EXPECT(pdef && strstr(pdef, "- edit_file(path, old_string, new_string, "
"replace_all?):"),
"default prompt lists edit_file with optional marker");
EXPECT(pdef && strstr(pdef, "list_dir, glob, grep"), "default workflow");
EXPECT(pdef && strstr(pdef, "must be quoted"), "ctl numeric rule");
EXPECT(pdef && strstr(pdef, "Always run `make test`."), "project notes");
free(pdef);
/* /tools off: grep disappears everywhere */
cfg.tools_off = 1ULL << cst_tool_index("grep");
EXPECT(!cst_tool_active(&cfg, cst_tool_find("grep")), "grep off");
cJSON *off = cst_tools_schema_cfg(&cfg);
EXPECT(!schema_has(off, "grep") && schema_has(off, "glob"), "schema off");
cJSON_Delete(off);
char *names = cst_tools_names(&cfg);
EXPECT(names && !strstr(names, "grep") && strstr(names, "glob"),
"names off");
free(names);
char *poff = cst_agent_build_prompt(&cfg, CST_PROTO_NATIVE_EOS, NULL);
EXPECT(poff && !strstr(poff, "- grep(") && strstr(poff, "list_dir, glob)"),
"prompt off");
free(poff);
int prof = -1;
EXPECT(cst_tool_profile_parse("minimal", &prof) == 0 &&
prof == CST_TOOLS_MINIMAL,
"parse minimal");
EXPECT(cst_tool_profile_parse("bogus", &prof) != 0, "parse bogus");
if (nfail) {
fprintf(stderr, "test_registry: %d failure(s)\n", nfail);
return 1;
}
printf("test_registry: OK\n");
return 0;
}
+295
View File
@@ -0,0 +1,295 @@
#define _POSIX_C_SOURCE 200809L
#include "cJSON.h"
#include "ctl.h"
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/*
* Truncation experiment on recorded model streams (tests/fixtures/sse).
*
* For every recorded assistant message we rebuild the logical stream the
* runtime sees and cut it at every byte:
* - CTL traces: the content text; executable calls = CALL_READY events.
* - native traces: tool calls as a JSON array of {"name","arguments"};
* json-whole : execute only when the whole array parses (EOS baseline)
* json-object: execute each object once it parses (incremental JSON)
*
* Checked properties (the CST soundness claims):
* S1 no call is executable on a prefix unless its bytes are complete;
* S2 every call executed on a prefix is identical to the same call in the
* full stream (prefix consistency, i.e. execution is never revoked);
* S3 the number of executable calls is monotone in the prefix length.
* The printed table reports how early calls become executable (experiment C).
*/
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
typedef struct {
char *buf;
size_t len, cap;
} sbuf_t;
static void sb_add(sbuf_t *b, const char *s, size_t n) {
if (b->len + n + 1 > b->cap) {
size_t nc = b->cap ? b->cap * 2 : 1024;
while (nc < b->len + n + 1)
nc *= 2;
b->buf = realloc(b->buf, nc);
b->cap = nc;
}
memcpy(b->buf + b->len, s, n);
b->len += n;
b->buf[b->len] = 0;
}
#define MAXC 32
typedef struct {
sbuf_t content;
int ncalls;
char names[MAXC][64];
sbuf_t args[MAXC];
} trace_t;
static void trace_free(trace_t *t) {
free(t->content.buf);
for (int i = 0; i < MAXC; i++)
free(t->args[i].buf);
memset(t, 0, sizeof(*t));
}
static int load_trace(const char *path, trace_t *t) {
memset(t, 0, sizeof(*t));
FILE *fp = fopen(path, "rb");
if (!fp)
return -1;
char *line = NULL;
size_t cap = 0;
ssize_t n;
while ((n = getline(&line, &cap, fp)) > 0) {
if (strncmp(line, "data:", 5) != 0)
continue;
cJSON *j = cJSON_Parse(line + 5);
if (!j)
continue;
const cJSON *ch = cJSON_GetArrayItem(cJSON_GetObjectItem(j, "choices"), 0);
const cJSON *d = cJSON_GetObjectItem(ch, "delta");
const cJSON *c = cJSON_GetObjectItem(d, "content");
if (cJSON_IsString(c))
sb_add(&t->content, c->valuestring, strlen(c->valuestring));
const cJSON *tc = NULL;
cJSON_ArrayForEach(tc, cJSON_GetObjectItem(d, "tool_calls")) {
const cJSON *ji = cJSON_GetObjectItem(tc, "index");
int idx = cJSON_IsNumber(ji) ? ji->valueint : 0;
if (idx < 0 || idx >= MAXC)
continue;
if (idx >= t->ncalls)
t->ncalls = idx + 1;
const cJSON *fn = cJSON_GetObjectItem(tc, "function");
const cJSON *nm = cJSON_GetObjectItem(fn, "name");
const cJSON *ar = cJSON_GetObjectItem(fn, "arguments");
if (cJSON_IsString(nm) && nm->valuestring[0])
snprintf(t->names[idx], sizeof(t->names[idx]), "%s", nm->valuestring);
if (cJSON_IsString(ar))
sb_add(&t->args[idx], ar->valuestring, strlen(ar->valuestring));
}
cJSON_Delete(j);
}
free(line);
fclose(fp);
return 0;
}
/* --- CTL side ------------------------------------------------------------- */
typedef struct {
int n;
char sig[MAXC][512]; /* id|name|k=v... (truncated) as identity */
} ready_t;
static void on_ctl(const ctl_event_t *ev, void *ud) {
ready_t *r = ud;
if (ev->type != CTL_EV_CALL_READY || r->n >= MAXC)
return;
char *s = r->sig[r->n++];
int o = snprintf(s, 512, "%s|%s", ev->call->id, ev->call->name);
for (int i = 0; i < ev->call->nargs && o < 500; i++)
o += snprintf(s + o, (size_t)(512 - o), "|%s=%.*s", ev->call->args[i].key,
(int)(ev->call->args[i].value_len > 200
? 200
: ev->call->args[i].value_len),
ev->call->args[i].value);
}
static void ctl_ready(const char *s, size_t n, int finish, ready_t *r) {
memset(r, 0, sizeof(*r));
ctl_parser_t *p = ctl_parser_create(on_ctl, r);
ctl_parser_feed(p, s, n);
if (finish)
ctl_parser_finish(p);
ctl_parser_destroy(p);
}
/* --- JSON side ------------------------------------------------------------ */
static void build_json_stream(const trace_t *t, sbuf_t *out) {
sb_add(out, "[", 1);
for (int i = 0; i < t->ncalls; i++) {
char head[160];
int hn = snprintf(head, sizeof(head), "%s{\"name\":\"%s\",\"arguments\":",
i ? "," : "", t->names[i]);
sb_add(out, head, (size_t)hn);
if (t->args[i].len)
sb_add(out, t->args[i].buf, t->args[i].len);
else
sb_add(out, "{}", 2);
sb_add(out, "}", 1);
}
sb_add(out, "]", 1);
}
static int json_whole(const char *s, size_t n) {
cJSON *j = cJSON_ParseWithLength(s, n);
int k = cJSON_IsArray(j) ? cJSON_GetArraySize(j) : 0;
cJSON_Delete(j);
return k;
}
static int json_objects(const char *s, size_t n) {
int k = 0;
size_t off = 0;
while (off < n) {
cst_json_tool_t t;
const char *p = memchr(s + off, '{', n - off);
if (!p)
break;
if (!cst_json_try_parse_tool(p, n - (size_t)(p - s), &t))
break;
k++;
/* skip past this object */
const char *end = NULL;
cJSON *j = cJSON_ParseWithLengthOpts(p, n - (size_t)(p - s), &end, 0);
cJSON_Delete(j);
if (!end || end <= p)
break;
off = (size_t)(end - s);
}
return k;
}
/* --- experiment ----------------------------------------------------------- */
static void report_row(const char *label, size_t n, const int *at_pct,
int total) {
printf(" %-26s bytes=%-6zu calls=%d executable@25/50/75/90/100%%: "
"%d/%d/%d/%d/%d\n",
label, n, total, at_pct[0], at_pct[1], at_pct[2], at_pct[3],
at_pct[4]);
}
static const int PCTS[5] = {25, 50, 75, 90, 100};
static void run_ctl(const char *label, const sbuf_t *c) {
ready_t full;
ctl_ready(c->buf, c->len, 1, &full);
int prev = 0, at[5] = {0};
size_t first_ready_at = 0;
for (size_t cut = 0; cut <= c->len; cut++) {
ready_t r;
ctl_ready(c->buf, cut, cut == c->len, &r);
EXPECT(r.n >= prev, "S3 monotone");
EXPECT(r.n <= full.n, "S1 never more than the full stream");
for (int i = 0; i < r.n && i < full.n; i++)
EXPECT(strcmp(r.sig[i], full.sig[i]) == 0, "S2 prefix-consistent call");
if (r.n > 0 && prev == 0)
first_ready_at = cut;
prev = r.n;
for (int k = 0; k < 5; k++)
if (cut == c->len * (size_t)PCTS[k] / 100)
at[k] = r.n;
}
report_row(label, c->len, at, full.n);
if (full.n > 0)
printf(" %-26s first call executable at byte %zu (%.0f%% of message)\n",
"", first_ready_at, 100.0 * (double)first_ready_at / (double)c->len);
}
static void run_native(const char *label, const trace_t *t) {
sbuf_t js = {0};
build_json_stream(t, &js);
int whole_full = json_whole(js.buf, js.len);
int obj_full = json_objects(js.buf, js.len);
EXPECT(whole_full == t->ncalls, "json-whole parses at full length");
EXPECT(obj_full == t->ncalls, "json-object parses all at full length");
int at_w[5] = {0}, at_o[5] = {0}, prev_o = 0;
for (size_t cut = 0; cut <= js.len; cut++) {
int w = json_whole(js.buf, cut);
int o = json_objects(js.buf, cut);
if (cut < js.len)
EXPECT(w == 0, "json-whole executes nothing before the end");
EXPECT(o >= prev_o, "S3 monotone (json-object)");
prev_o = o;
for (int k = 0; k < 5; k++) {
if (cut == js.len * (size_t)PCTS[k] / 100) {
at_w[k] = w;
at_o[k] = o;
}
}
}
char l[80];
snprintf(l, sizeof(l), "%s json-whole", label);
report_row(l, js.len, at_w, whole_full);
snprintf(l, sizeof(l), "%s json-object", label);
report_row(l, js.len, at_o, obj_full);
free(js.buf);
}
int main(void) {
static const char *dirs[] = {"tests/fixtures/sse/deepseek-ctl-heredoc",
"tests/fixtures/sse/deepseek-ctl",
"tests/fixtures/sse/deepseek-native-eos"};
int traces = 0;
for (size_t d = 0; d < sizeof(dirs) / sizeof(dirs[0]); d++) {
printf("%s\n", dirs[d]);
for (int seq = 0; seq < 64; seq++) {
char path[512];
snprintf(path, sizeof(path), "%s/%04d.sse", dirs[d], seq);
if (access(path, R_OK) != 0)
break;
trace_t t;
if (load_trace(path, &t) != 0)
continue;
traces++;
char label[64];
snprintf(label, sizeof(label), "#%04d", seq);
if (t.ncalls > 0)
run_native(label, &t);
else if (t.content.len > 0 && strstr(t.content.buf, "call "))
run_ctl(label, &t.content);
else
printf(" %-26s (final answer, no calls)\n", label);
trace_free(&t);
}
}
if (traces == 0)
printf("(no recorded traces found; run from the repo root)\n");
if (nfail) {
fprintf(stderr, "test_replay_real: %d failed\n", nfail);
return 1;
}
printf("test_replay_real: OK\n");
return 0;
}
+157
View File
@@ -0,0 +1,157 @@
#define _POSIX_C_SOURCE 200809L
#include "cJSON.h"
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
static const char *TRICKY = "tab\there \"q\" back\\slash\nnew line \x01 中文";
static const char *TC = "[{\"id\":\"c1\",\"type\":\"function\",\"function\":{"
"\"name\":\"read_file\",\"arguments\":"
"\"{\\\"path\\\":\\\"a.c\\\"}\"}}]";
/* compare JSON texts structurally */
static int json_eq(const char *a, const char *b) {
cJSON *ja = cJSON_Parse(a), *jb = cJSON_Parse(b);
int eq = ja && jb && cJSON_Compare(ja, jb, 1);
cJSON_Delete(ja);
cJSON_Delete(jb);
return eq;
}
static void append(cst_session_t *s, cst_role_t role, const char *content,
const char *tid, const char *tname, const char *tc,
const char *meta) {
cst_message_t m;
memset(&m, 0, sizeof(m));
m.role = role;
m.content = (char *)content;
if (tid)
snprintf(m.tool_call_id, sizeof(m.tool_call_id), "%s", tid);
if (tname)
snprintf(m.tool_name, sizeof(m.tool_name), "%s", tname);
m.tool_calls_json = (char *)tc;
m.meta_json = (char *)meta;
EXPECT(cst_session_append(s, &m) == 0, "append");
}
static void check_messages(cst_session_t *s) {
EXPECT(cst_session_count(s) == 5, "5 messages");
const cst_message_t *m = cst_session_get(s, 0);
EXPECT(m && m->role == CST_ROLE_SYSTEM, "system role");
m = cst_session_get(s, 1);
EXPECT(m && m->role == CST_ROLE_USER && strcmp(m->content, TRICKY) == 0,
"tricky user text exact");
m = cst_session_get(s, 2);
EXPECT(m && m->role == CST_ROLE_ASSISTANT, "assistant role");
EXPECT(m && m->tool_calls_json && json_eq(m->tool_calls_json, TC),
"tool_calls preserved");
EXPECT(m && m->meta_json && json_eq(m->meta_json, "{\"protocol\":\"native-eos\"}"),
"assistant meta");
m = cst_session_get(s, 3);
EXPECT(m && m->role == CST_ROLE_TOOL && strcmp(m->tool_call_id, "c1") == 0 &&
strcmp(m->tool_name, "read_file") == 0,
"tool id/name");
EXPECT(m && strcmp(m->content, "int a;\n\tx\n") == 0, "tool content");
m = cst_session_get(s, 4);
EXPECT(m && m->role == CST_ROLE_USER && strcmp(m->tool_call_id, "c2") == 0,
"text-protocol result frame keeps tool_call_id");
EXPECT(m && m->meta_json &&
json_eq(m->meta_json,
"{\"kind\":\"tool_result\",\"protocol\":\"ctl\"}"),
"result frame meta");
}
int main(void) {
char path[256];
snprintf(path, sizeof(path), "/tmp/cst-session-test-%d.jsonl", (int)getpid());
remove(path);
cst_session_t *s = cst_session_open(path);
EXPECT(s != NULL, "open new");
if (!s)
return 1;
append(s, CST_ROLE_SYSTEM, "sys", NULL, NULL, NULL, NULL);
append(s, CST_ROLE_USER, TRICKY, NULL, NULL, NULL, NULL);
append(s, CST_ROLE_ASSISTANT, "", NULL, NULL, TC,
"{\"protocol\":\"native-eos\"}");
append(s, CST_ROLE_TOOL, "int a;\n\tx\n", "c1", "read_file", NULL, NULL);
append(s, CST_ROLE_USER, "result c2 exit=0 2\nok\nend", "c2", "run_shell",
NULL, "{\"kind\":\"tool_result\",\"protocol\":\"ctl\"}");
check_messages(s);
cst_session_close(s);
/* reload from disk */
s = cst_session_open(path);
EXPECT(s != NULL, "reopen");
if (!s)
return 1;
check_messages(s);
/* every line on disk is standalone valid JSON with tool_calls as array */
FILE *fp = fopen(path, "r");
char line[8192];
int lines = 0, arrays = 0;
while (fp && fgets(line, sizeof(line), fp)) {
cJSON *j = cJSON_Parse(line);
EXPECT(j != NULL, "jsonl line parses");
if (cJSON_IsArray(cJSON_GetObjectItemCaseSensitive(j, "tool_calls")))
arrays++;
cJSON_Delete(j);
lines++;
}
if (fp)
fclose(fp);
EXPECT(lines == 5 && arrays == 1, "5 lines, tool_calls stored as array");
/* rewrite + clear(keep_system) */
const cst_message_t *first = cst_session_get(s, 0);
cst_message_t keep[2];
keep[0] = *first;
keep[1] = *cst_session_get(s, 1);
EXPECT(cst_session_rewrite(s, keep, 2) == 0, "rewrite");
EXPECT(cst_session_count(s) == 2, "rewrite count");
EXPECT(cst_session_clear(s, 1) == 0 && cst_session_count(s) == 1,
"clear keeps system");
cst_session_close(s);
/* legacy format: tool_calls_json stored as a string */
fp = fopen(path, "w");
fprintf(fp, "{\"role\":\"assistant\",\"content\":\"\",\"tool_calls_json\":"
"\"[{\\\"id\\\":\\\"c9\\\",\\\"type\\\":\\\"function\\\","
"\\\"function\\\":{\\\"name\\\":\\\"run_shell\\\","
"\\\"arguments\\\":\\\"{}\\\"}}]\"}\n");
fprintf(fp, "not json at all\n");
fprintf(fp, "{\"role\":\"tool\",\"content\":\"out\",\"tool_call_id\":\"c9\"}\n");
fclose(fp);
s = cst_session_open(path);
EXPECT(s != NULL, "open legacy");
if (s) {
EXPECT(cst_session_count(s) == 2, "legacy: bad line skipped");
const cst_message_t *m = cst_session_get(s, 0);
EXPECT(m && m->tool_calls_json && strstr(m->tool_calls_json, "c9"),
"legacy tool_calls_json read");
cst_session_close(s);
}
remove(path);
if (nfail) {
fprintf(stderr, "test_session_roundtrip: %d failed\n", nfail);
return 1;
}
printf("test_session_roundtrip: OK\n");
return 0;
}
+94
View File
@@ -0,0 +1,94 @@
#define _POSIX_C_SOURCE 200809L
#include "cst_agent.h"
#include "os_compat.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
/* run_shell must never hang the agent: timeout kills the whole process
* group, stdin is empty, huge output is capped. */
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
static char g_ws[256];
static int sh(const char *cmd, cst_tool_result_t *r, double *ms) {
ctl_tool_call_t c;
memset(&c, 0, sizeof(c));
snprintf(c.id, sizeof(c.id), "s");
snprintf(c.name, sizeof(c.name), "run_shell");
snprintf(c.args[0].key, sizeof(c.args[0].key), "cmd");
c.args[0].value = (char *)cmd;
c.args[0].value_len = strlen(cmd);
c.nargs = 1;
memset(r, 0, sizeof(*r));
double t0 = os_now_ms();
cst_tool_dispatch(g_ws, &c, r);
*ms = os_now_ms() - t0;
return r->exit_code;
}
int main(void) {
snprintf(g_ws, sizeof(g_ws), "/tmp/cst-shell-test-%d", (int)getpid());
mkdir(g_ws, 0755);
cst_tool_result_t r;
double ms;
EXPECT(sh("echo hi; exit 3", &r, &ms) == 3, "exit code passed through");
EXPECT(r.out && strstr(r.out, "hi"), "stdout captured");
cst_tool_result_free(&r);
/* stdin is /dev/null: a command reading stdin returns at once */
sh("cat; echo done", &r, &ms);
EXPECT(r.out && strstr(r.out, "done") && ms < 5000, "stdin is empty");
cst_tool_result_free(&r);
/* timeout kills the process group, including background children that
* keep the output pipe open */
setenv("CST_SHELL_TIMEOUT", "1", 1);
os_set_run_timeout(1);
int ec = sh("sleep 30 & sleep 30; echo never", &r, &ms);
EXPECT(ec == 124, "timeout exit code 124");
EXPECT(ms < 8000, "timeout returns promptly");
EXPECT(!r.out || !strstr(r.out, "never"), "command was stopped");
cst_tool_result_free(&r);
os_set_run_timeout(0);
unsetenv("CST_SHELL_TIMEOUT");
/* the shell exits but leaves a background child holding the pipe */
ec = sh("sleep 30 & echo started", &r, &ms);
EXPECT(ec == 0, "shell exit code kept when a background child lingers");
EXPECT(r.out && strstr(r.out, "started"), "foreground output kept");
EXPECT(ms < 5000, "lingering background child does not block");
cst_tool_result_free(&r);
/* huge output: capped and marked, the child is not blocked on the pipe */
sh("head -c 5000000 /dev/zero | tr '\\0' x; echo; echo tail-marker", &r, &ms);
EXPECT(r.out && r.out_len < 2000000, "output capped");
EXPECT(r.out && strstr(r.out, "truncated"), "truncation noted");
EXPECT(ms < 20000, "large output does not hang");
cst_tool_result_free(&r);
char cmd[300];
snprintf(cmd, sizeof(cmd), "rm -rf %s", g_ws);
if (system(cmd) != 0)
fprintf(stderr, "warning: cleanup failed\n");
if (nfail) {
fprintf(stderr, "test_shell: %d failed\n", nfail);
return 1;
}
printf("test_shell: OK\n");
return 0;
}
+301
View File
@@ -0,0 +1,301 @@
#define _POSIX_C_SOURCE 200809L
#include "cJSON.h"
#include "cst_agent.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Drives cst_llm_chat_stream through CST_REPLAY_SSE with synthetic and
* recorded (tests/fixtures/sse) streams, at several network chunk sizes. */
static int nfail;
#define EXPECT(cond, msg) \
do { \
if (!(cond)) { \
fprintf(stderr, "FAIL %s:%d: %s\n", __FILE__, __LINE__, msg); \
nfail++; \
} \
} while (0)
#define MAX_CALLS 16
typedef struct {
char text[65536];
size_t text_len;
int ncalls;
char ids[MAX_CALLS][64];
char names[MAX_CALLS][64];
char *args[MAX_CALLS]; /* NULL = invalid */
int args_null[MAX_CALLS];
int nopen;
int open_before_first_call; /* opens seen before the first call fired */
} capture_t;
static void on_delta(const char *d, size_t n, void *ud) {
capture_t *c = ud;
if (c->text_len + n < sizeof(c->text)) {
memcpy(c->text + c->text_len, d, n);
c->text_len += n;
c->text[c->text_len] = 0;
}
}
static void on_tool(const char *id, const char *name, const char *args,
void *ud) {
capture_t *c = ud;
if (c->ncalls >= MAX_CALLS)
return;
int i = c->ncalls++;
snprintf(c->ids[i], sizeof(c->ids[i]), "%s", id ? id : "");
snprintf(c->names[i], sizeof(c->names[i]), "%s", name ? name : "");
c->args[i] = args ? strdup(args) : NULL;
c->args_null[i] = args == NULL;
if (i == 0)
c->open_before_first_call = c->nopen;
}
static void on_open(int index, const char *id, const char *name, void *ud) {
(void)index;
(void)id;
(void)name;
capture_t *c = ud;
c->nopen++;
}
static void capture_free(capture_t *c) {
for (int i = 0; i < c->ncalls; i++)
free(c->args[i]);
}
static char g_dir[256];
static void write_fixture(const char *sse) {
char path[512];
snprintf(path, sizeof(path), "%s/0000.sse", g_dir);
FILE *fp = fopen(path, "wb");
fputs(sse, fp);
fclose(fp);
}
static int replay(const char *dir, int seq, const char *chunk, capture_t *c,
cst_llm_stats_t *st) {
memset(c, 0, sizeof(*c));
memset(st, 0, sizeof(*st));
setenv("CST_REPLAY_SSE", dir, 1);
setenv("CST_REPLAY_CHUNK", chunk, 1);
cst_llm_trace_reset();
for (int i = 0; i < seq; i++) {
/* advance the sequence counter to the wanted file */
cst_llm_request_t skip;
memset(&skip, 0, sizeof(skip));
skip.api_base = "http://replay";
skip.model = "m";
char e[64];
capture_t tmp;
memset(&tmp, 0, sizeof(tmp));
skip.userdata = &tmp;
cst_llm_chat_stream(&skip, e, sizeof(e));
}
cst_llm_request_t r;
memset(&r, 0, sizeof(r));
r.api_base = "http://replay";
r.model = "m";
r.on_delta = on_delta;
r.on_native_tool = on_tool;
r.on_native_open = on_open;
r.userdata = c;
r.stats = st;
char err[256];
return cst_llm_chat_stream(&r, err, sizeof(err));
}
static const char *CHUNKS[] = {"1", "7", "64", "4096"};
#define NCHUNKS 4
static void test_text_reasoning_usage(void) {
write_fixture(
"data: {\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\","
"\"reasoning_content\":\"plan \"}}]}\n\n"
"data: {\"choices\":[{\"index\":0,\"delta\":{\"reasoning_content\":"
"\"more\"}}]}\r\n\r\n"
": keep-alive comment\n\n"
"data: {\"choices\":[{\"index\":0,\"delta\":{\"content\":\"Hel\"}}]}\n\n"
"data:{\"choices\":[{\"index\":0,\"delta\":{\"content\":\"lo\\t\\u4e2d\"},"
"\"finish_reason\":\"stop\"}]}\n\n"
"data: {\"choices\":[],\"usage\":{\"prompt_tokens\":11,"
"\"completion_tokens\":5,\"prompt_cache_hit_tokens\":8}}\n\n"
"data: [DONE]\n\n");
for (int k = 0; k < NCHUNKS; k++) {
capture_t c;
cst_llm_stats_t st;
int rc = replay(g_dir, 0, CHUNKS[k], &c, &st);
EXPECT(rc == 0, "text stream ok");
EXPECT(strcmp(c.text, "<think>plan more</think>Hello\t\xe4\xb8\xad") == 0,
"reasoning wrapped + content decoded");
EXPECT(strcmp(st.finish_reason, "stop") == 0, "finish_reason");
EXPECT(st.prompt_tokens == 11 && st.completion_tokens == 5 &&
st.cached_tokens == 8,
"usage incl. deepseek cache hits");
EXPECT(c.ncalls == 0, "no calls");
capture_free(&c);
}
}
/* Two parallel calls, fragmented arguments, no [DONE] and no final newline. */
static void test_parallel_calls(void) {
write_fixture(
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":"
"\"c1\",\"type\":\"function\",\"function\":{\"name\":\"read_file\","
"\"arguments\":\"\"}}]}}]}\n\n"
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,"
"\"function\":{\"arguments\":\"{\\\"pa\"}}]}}]}\n\n"
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,"
"\"function\":{\"arguments\":\"th\\\":\\\"a.c\\\"}\"}}]}}]}\n\n"
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":1,\"id\":"
"\"c2\",\"function\":{\"name\":\"run_shell\",\"arguments\":"
"\"{\\\"cmd\\\":\"}}]}}]}\n\n"
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":1,"
"\"function\":{\"arguments\":\"\\\"make\\\\ttest\\\"}\"}}]}}]}\n\n"
"data: {\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\"}]}");
for (int k = 0; k < NCHUNKS; k++) {
capture_t c;
cst_llm_stats_t st;
int rc = replay(g_dir, 0, CHUNKS[k], &c, &st);
EXPECT(rc == 0, "parallel stream ok");
EXPECT(c.ncalls == 2, "two calls");
if (c.ncalls == 2) {
EXPECT(strcmp(c.ids[0], "c1") == 0 && strcmp(c.names[0], "read_file") == 0,
"call 1 id/name");
EXPECT(c.args[0] && strcmp(c.args[0], "{\"path\":\"a.c\"}") == 0,
"call 1 args reassembled");
EXPECT(strcmp(c.ids[1], "c2") == 0 && strcmp(c.names[1], "run_shell") == 0,
"call 2 id/name");
EXPECT(c.args[1] && strcmp(c.args[1], "{\"cmd\":\"make\\ttest\"}") == 0,
"call 2 args reassembled");
/* native-inc: call 1 is complete as soon as index 1 starts, before its
* open event and long before finish_reason */
EXPECT(c.open_before_first_call == 1, "call 1 fired when index 1 began");
}
EXPECT(c.nopen == 2, "two opens");
EXPECT(strcmp(st.finish_reason, "tool_calls") == 0, "finish tool_calls");
capture_free(&c);
}
}
/* Gateways that reuse index 0 for every call but change the id. */
static void test_same_index_new_id(void) {
write_fixture(
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":"
"\"a\",\"function\":{\"name\":\"read_file\",\"arguments\":"
"\"{\\\"path\\\":\\\"x\\\"}\"}}]}}]}\n\n"
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":"
"\"b\",\"function\":{\"name\":\"read_file\",\"arguments\":"
"\"{\\\"path\\\":\\\"y\\\"}\"}}]}}]}\n\n"
"data: [DONE]\n\n");
capture_t c;
cst_llm_stats_t st;
replay(g_dir, 0, "4096", &c, &st);
EXPECT(c.ncalls == 2, "same index, new id -> two calls");
if (c.ncalls == 2)
EXPECT(c.args[1] && strstr(c.args[1], "\"y\""), "second call args");
capture_free(&c);
}
static void test_invalid_args_and_error(void) {
write_fixture(
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":"
"\"c1\",\"function\":{\"name\":\"write_file\",\"arguments\":"
"\"{\\\"path\\\":\\\"a\"}}]}}]}\n\n"
"data: [DONE]\n\n");
capture_t c;
cst_llm_stats_t st;
replay(g_dir, 0, "3", &c, &st);
EXPECT(c.ncalls == 1 && c.args_null[0], "truncated args -> NULL");
capture_free(&c);
write_fixture("data: {\"error\":{\"message\":\"quota exceeded\"}}\n\n");
int rc = replay(g_dir, 0, "4096", &c, &st);
EXPECT(rc != 0, "in-stream error fails the request");
EXPECT(strstr(st.error, "quota exceeded") != NULL, "error message kept");
capture_free(&c);
}
static int file_exists(const char *p) { return access(p, R_OK) == 0; }
/* Recorded DeepSeek traces: every call must decode to a JSON object and the
* decoded result must not depend on the network chunking. */
static void test_recorded(void) {
const char *dir = "tests/fixtures/sse/deepseek-native-eos";
char p[512];
snprintf(p, sizeof(p), "%s/0000.sse", dir);
if (!file_exists(p)) {
printf(" (skip recorded traces: %s missing)\n", p);
return;
}
int total_calls = 0;
for (int seq = 0; seq < 16; seq++) {
snprintf(p, sizeof(p), "%s/%04d.sse", dir, seq);
if (!file_exists(p))
break;
capture_t ref;
cst_llm_stats_t st;
EXPECT(replay(dir, seq, "4096", &ref, &st) == 0, "recorded replay ok");
for (int i = 0; i < ref.ncalls; i++) {
cJSON *j = ref.args[i] ? cJSON_Parse(ref.args[i]) : NULL;
EXPECT(cJSON_IsObject(j), "recorded call args are a JSON object");
EXPECT(cst_tool_known(ref.names[i]), "recorded call names a known tool");
cJSON_Delete(j);
}
total_calls += ref.ncalls;
EXPECT(st.prompt_tokens > 0, "recorded usage present");
capture_t one;
replay(dir, seq, "1", &one, &st);
EXPECT(one.ncalls == ref.ncalls, "chunking does not change calls");
EXPECT(one.text_len == ref.text_len &&
memcmp(one.text, ref.text, ref.text_len) == 0,
"chunking does not change text");
for (int i = 0; i < one.ncalls && i < ref.ncalls; i++)
EXPECT(one.args[i] && ref.args[i] && strcmp(one.args[i], ref.args[i]) == 0,
"chunking does not change args");
capture_free(&one);
capture_free(&ref);
}
EXPECT(total_calls > 0, "recorded native trace contains tool calls");
const char *hd = "tests/fixtures/sse/deepseek-ctl-heredoc";
snprintf(p, sizeof(p), "%s/0000.sse", hd);
if (file_exists(p)) {
capture_t c;
cst_llm_stats_t st;
replay(hd, 0, "5", &c, &st);
EXPECT(strstr(c.text, "call ") != NULL, "ctl trace carries CTL text");
EXPECT(c.ncalls == 0, "text protocol: no native calls");
capture_free(&c);
}
}
int main(void) {
snprintf(g_dir, sizeof(g_dir), "/tmp/cst-sse-test-%d", (int)getpid());
char cmd[300];
snprintf(cmd, sizeof(cmd), "mkdir -p %s", g_dir);
if (system(cmd) != 0)
return 1;
test_text_reasoning_usage();
test_parallel_calls();
test_same_index_new_id();
test_invalid_args_and_error();
test_recorded();
snprintf(cmd, sizeof(cmd), "rm -rf %s", g_dir);
if (system(cmd) != 0)
fprintf(stderr, "warning: cleanup failed\n");
if (nfail) {
fprintf(stderr, "test_sse_decode: %d failed\n", nfail);
return 1;
}
printf("test_sse_decode: OK\n");
return 0;
}

Some files were not shown because too many files have changed in this diff Show More