forked from WHC/Hermit
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:
@@ -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
@@ -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
@@ -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}")
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 实验)。
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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 的时延数字(受网络与服务端负载影响)
|
||||||
|
|||||||
@@ -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` 给出的行号附近查找,行号不准时退化为搜索,并容忍行尾空白差异。
|
||||||
|
|||||||
@@ -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.
|
||||||
Executable
+34
@@ -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"
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
first
|
||||||
|
second
|
||||||
|
third
|
||||||
@@ -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)"
|
||||||
@@ -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` 验证;若失败,读输出修正后再验证。最后用一句话说明结果。
|
||||||
Executable
+4
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
make check
|
||||||
@@ -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. 用一句话说明结果。
|
||||||
Executable
+10
@@ -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
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("hello, cst\n");
|
||||||
|
printf("tab\there\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
hello, cst
|
||||||
|
tab here
|
||||||
Executable
+5
@@ -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
|
||||||
@@ -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)
|
||||||
@@ -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.
|
||||||
Executable
+11
@@ -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
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
#ifndef REPORT_H
|
||||||
|
#define REPORT_H
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void print_report(FILE *out, const int *prices, int n);
|
||||||
|
|
||||||
|
#endif
|
||||||
Executable
+28
@@ -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"
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
make snake
|
||||||
|
test -x snake
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
# 任务:先跑测试,再修,再跑测试
|
||||||
|
|
||||||
|
1. 先运行 `make test`,看哪些用例失败(此时不要改代码)。
|
||||||
|
2. 阅读 `strutil.c`,修复导致失败的 bug(不要修改 `tests/` 下的文件)。
|
||||||
|
3. 再次运行 `make test`,确认全部通过。
|
||||||
|
4. 用简短中文说明你修了什么。
|
||||||
Executable
+12
@@ -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
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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 ."
|
|
||||||
|
|||||||
Executable
+148
@@ -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
@@ -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
|
|
||||||
|
|||||||
Executable
+63
@@ -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"
|
||||||
Executable
+186
@@ -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))
|
||||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+316
-56
@@ -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) {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
@@ -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, ©[i]) != 0 || write_line(s->fp, ©[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
@@ -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);
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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[] = {{"&", "&"}, {"<", "<"}, {">", ">"},
|
||||||
|
{""", "\""}, {"'", "'"}, {"'", "'"},
|
||||||
|
{" ", " "}};
|
||||||
|
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);
|
||||||
|
}
|
||||||
+296
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]
|
||||||
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
Reference in New Issue
Block a user