feat: AutoZero 通用 Agent 框架(从 AutoGo 五阶段拆分完成)
llm(三协议 Provider)/ session(JSONL 持久化)/ tools(Registry、围栏、 builtin、computer)/ cdp(browser)/ skills / mcp / agent(循环、两级压缩、 编排、审批门控)+ 底座 jsonl/textx/execx/diag/scratch/spill/imagecache。 产品装配代码(config 桥接、schedule 工具、memory/plan/retro/prompt) 留在 AutoGo,宿主接入方式见 MIGRATION.md。
This commit is contained in:
commit
d5dac5a6fc
140 files changed
+26497
No files matched your search
@@ -0,0 +1,90 @@
|
||||
package llm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func sseProviderServer(t *testing.T, sse string) *httptest.Server {
|
||||
t.Helper()
|
||||
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
_, _ = w.Write([]byte(sse))
|
||||
}))
|
||||
}
|
||||
|
||||
func collectToolCallDeltas(t *testing.T, p Provider) []ToolCall {
|
||||
t.Helper()
|
||||
var got []ToolCall
|
||||
_, err := p.Stream(context.Background(), "s", []Message{{Role: RoleUser, Content: "hi"}}, nil, StreamOptions{},
|
||||
func(d StreamDelta) {
|
||||
if d.ToolCall != nil {
|
||||
got = append(got, *d.ToolCall)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return got
|
||||
}
|
||||
|
||||
// TestAnthropicEmitsCompletedToolCall:content_block_stop 一到就提前通知,
|
||||
// 不等 message_stop。
|
||||
func TestAnthropicEmitsCompletedToolCall(t *testing.T) {
|
||||
srv := sseProviderServer(t, ""+
|
||||
"data: {\"type\":\"message_start\",\"message\":{\"usage\":{\"input_tokens\":10}}}\n\n"+
|
||||
"data: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"tool_use\",\"id\":\"t1\",\"name\":\"read\"}}\n\n"+
|
||||
"data: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"input_json_delta\",\"partial_json\":\"{\\\"path\\\":\\\"a\\\"}\"}}\n\n"+
|
||||
"data: {\"type\":\"content_block_stop\",\"index\":0}\n\n"+
|
||||
"data: {\"type\":\"message_stop\"}\n\n")
|
||||
defer srv.Close()
|
||||
p := &AnthropicProvider{BaseURL: srv.URL, APIKey: "k", Model: "m", MaxTokens: 128, HTTP: srv.Client()}
|
||||
got := collectToolCallDeltas(t, p)
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("tool call deltas = %d, want 1", len(got))
|
||||
}
|
||||
if got[0].ID != "t1" || got[0].Name != "read" || got[0].Input != `{"path":"a"}` {
|
||||
t.Fatalf("tool call = %+v", got[0])
|
||||
}
|
||||
}
|
||||
|
||||
// TestOpenAIEmitsCompletedToolCalls:下一个 index 出现时前一个调用完整;
|
||||
// finish_reason 时最后一个完整。两者都通过 delta 通道提前通知。
|
||||
func TestOpenAIEmitsCompletedToolCalls(t *testing.T) {
|
||||
srv := sseProviderServer(t, ""+
|
||||
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"id\":\"t1\",\"function\":{\"name\":\"read\",\"arguments\":\"{\\\"path\\\":\\\"\"}}]}}]}\n\n"+
|
||||
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":0,\"function\":{\"arguments\":\"a\\\"}\"}}]}}]}\n\n"+
|
||||
"data: {\"choices\":[{\"delta\":{\"tool_calls\":[{\"index\":1,\"id\":\"t2\",\"function\":{\"name\":\"read\",\"arguments\":\"{}\"}}]}}]}\n\n"+
|
||||
"data: {\"choices\":[{\"delta\":{},\"finish_reason\":\"tool_calls\"}]}\n\n"+
|
||||
"data: [DONE]\n\n")
|
||||
defer srv.Close()
|
||||
p := &OpenAIProvider{BaseURL: srv.URL, APIKey: "k", Model: "m", HTTP: srv.Client()}
|
||||
got := collectToolCallDeltas(t, p)
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("tool call deltas = %d, want 2: %+v", len(got), got)
|
||||
}
|
||||
if got[0].ID != "t1" || got[0].Input != `{"path":"a"}` {
|
||||
t.Fatalf("first call = %+v", got[0])
|
||||
}
|
||||
if got[1].ID != "t2" || got[1].Input != "{}" {
|
||||
t.Fatalf("second call = %+v", got[1])
|
||||
}
|
||||
}
|
||||
|
||||
// TestResponsesEmitsCompletedToolCall:output_item.done 的 parameters 已完整。
|
||||
func TestResponsesEmitsCompletedToolCall(t *testing.T) {
|
||||
srv := sseProviderServer(t, ""+
|
||||
"data: {\"type\":\"response.output_item.done\",\"item\":{\"type\":\"function_call\",\"id\":\"item_1\",\"call_id\":\"c1\",\"name\":\"read\",\"arguments\":\"{\\\"path\\\":\\\"a\\\"}\"}}\n\n"+
|
||||
"data: {\"type\":\"response.completed\",\"response\":{\"output\":[{\"type\":\"function_call\",\"call_id\":\"c1\",\"name\":\"read\",\"arguments\":\"{\\\"path\\\":\\\"a\\\"}\"}],\"usage\":{\"input_tokens\":1,\"output_tokens\":1}}}\n\n")
|
||||
defer srv.Close()
|
||||
p := &ResponsesProvider{BaseURL: srv.URL, APIKey: "k", Model: "m", HTTP: srv.Client()}
|
||||
got := collectToolCallDeltas(t, p)
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("tool call deltas = %d, want 1 (dedup by ID): %+v", len(got), got)
|
||||
}
|
||||
if got[0].ID != "c1" || got[0].Input != `{"path":"a"}` {
|
||||
t.Fatalf("tool call = %+v", got[0])
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user