From e78d2dea460298aaa503928c22ef437aa92b6a7b Mon Sep 17 00:00:00 2001 From: Delicious233 <101502465+DeliciousBuding@users.noreply.github.com> Date: Wed, 19 Aug 2026 04:21:22 +0800 Subject: [PATCH] =?UTF-8?q?refactor(edge):=20adapters=20SDK=20=E9=80=82?= =?UTF-8?q?=E9=85=8D=E5=99=A8=E5=BD=92=E7=BB=84=E5=AD=90=E5=8C=85=EF=BC=88?= =?UTF-8?q?#1760=20=E9=A6=96=E4=B8=AA=E5=A2=9E=E9=87=8F=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #1760 首个有界增量:把内聚的直连 HTTP SDK 适配器从 internal/adapters 根包(81 个平铺 .go 文件)归组到新子包 internal/adapters/sdk, 根包降至 69 个文件,降低认知负载。 迁移内容(12 个文件 + 延迟基准测试拆分): - anthropic_sdk{,_request,_sse,_types,_e2e_test}.go - openai_sdk{,_request,_sse,_types,_e2e_test}.go - sdk_common{,_test}.go(BuildCommand 哨兵命令助手) - adapter_latency_test.go 的 SDK 基准/测试随迁至 sdk 包; ClassifyComplexity 基准留在根包 - 新增 doc.go / aliases.go(合同类型与 BusEvent* 常量派生自 internal/orchestration,模式对齐既有叶子包 adapters/orchestrator) 为何无环(go list -deps 机器验证): - sdk → adapters 为单向依赖(ResolveModel / CtxRunContext / NewNonRecoverableParseError / SDKAdapterContext 仍留在根包); - 根包不再引用任何 SDK 符号:registry.go 只含字符串适配器 ID "anthropic-sdk"/"openai-sdk",构造函数调用点只在组合根 cmd/agenthub-edge(已改为 import sdk)。 暂不归组:sdk_fixture_mapper*.go 与根包存在双向耦合 (mapper 使用根包 BusEvent* 常量;根包 agentspec_fixture.go 构造 SDKFixture* 类型并调用其它适配器构造函数),强行下沉会形成 import cycle,留待后续增量解耦。 纯重组、无行为变化:go build ./... / go vet ./... / go test ./internal/adapters/... -short 全绿; scripts/verify/verify-orchestrator-deps.py 3/3 通过; cmd/agenthub-edge 与 internal/lifecycle 测试亦通过。 Co-authored-by: Cursor --- .../cmd/agenthub-edge/adapter_registry.go | 5 +- .../internal/adapters/adapter_latency_test.go | 83 +---------------- .../adapters/sdk/adapter_latency_test.go | 89 +++++++++++++++++++ edge-server/internal/adapters/sdk/aliases.go | 29 ++++++ .../adapters/{ => sdk}/anthropic_sdk.go | 14 +-- .../{ => sdk}/anthropic_sdk_e2e_test.go | 2 +- .../{ => sdk}/anthropic_sdk_request.go | 9 +- .../adapters/{ => sdk}/anthropic_sdk_sse.go | 10 ++- .../adapters/{ => sdk}/anthropic_sdk_types.go | 4 +- edge-server/internal/adapters/sdk/doc.go | 26 ++++++ .../internal/adapters/{ => sdk}/openai_sdk.go | 16 ++-- .../adapters/{ => sdk}/openai_sdk_e2e_test.go | 5 +- .../adapters/{ => sdk}/openai_sdk_request.go | 9 +- .../adapters/{ => sdk}/openai_sdk_sse.go | 8 +- .../adapters/{ => sdk}/openai_sdk_types.go | 4 +- .../internal/adapters/{ => sdk}/sdk_common.go | 2 +- .../adapters/{ => sdk}/sdk_common_test.go | 2 +- 17 files changed, 196 insertions(+), 121 deletions(-) create mode 100644 edge-server/internal/adapters/sdk/adapter_latency_test.go create mode 100644 edge-server/internal/adapters/sdk/aliases.go rename edge-server/internal/adapters/{ => sdk}/anthropic_sdk.go (94%) rename edge-server/internal/adapters/{ => sdk}/anthropic_sdk_e2e_test.go (99%) rename edge-server/internal/adapters/{ => sdk}/anthropic_sdk_request.go (90%) rename edge-server/internal/adapters/{ => sdk}/anthropic_sdk_sse.go (94%) rename edge-server/internal/adapters/{ => sdk}/anthropic_sdk_types.go (96%) create mode 100644 edge-server/internal/adapters/sdk/doc.go rename edge-server/internal/adapters/{ => sdk}/openai_sdk.go (92%) rename edge-server/internal/adapters/{ => sdk}/openai_sdk_e2e_test.go (99%) rename edge-server/internal/adapters/{ => sdk}/openai_sdk_request.go (92%) rename edge-server/internal/adapters/{ => sdk}/openai_sdk_sse.go (95%) rename edge-server/internal/adapters/{ => sdk}/openai_sdk_types.go (96%) rename edge-server/internal/adapters/{ => sdk}/sdk_common.go (96%) rename edge-server/internal/adapters/{ => sdk}/sdk_common_test.go (98%) diff --git a/edge-server/cmd/agenthub-edge/adapter_registry.go b/edge-server/cmd/agenthub-edge/adapter_registry.go index 23020d15c..a021ea3e2 100644 --- a/edge-server/cmd/agenthub-edge/adapter_registry.go +++ b/edge-server/cmd/agenthub-edge/adapter_registry.go @@ -7,6 +7,7 @@ import ( "github.com/agenthub/edge-server/internal/adapters" "github.com/agenthub/edge-server/internal/adapters/orchestrator" + "github.com/agenthub/edge-server/internal/adapters/sdk" "github.com/agenthub/edge-server/internal/edgehttp" ) @@ -146,7 +147,7 @@ func registerManifestAdapters(reg *adapters.Registry, cfg config) { func registerSDKAdapters(reg *adapters.Registry, cfg config) { if cfg.AnthropicSDKPath != "" { apiKey := resolveSDKAPIKey(cfg.AnthropicSDKPath, "ANTHROPIC_API_KEY") - a := adapters.NewAnthropicSDKAdapter(apiKey, cfg.AgentModel, edgehttp.NewClient(adapters.AnthropicHTTPTimeout)) + a := sdk.NewAnthropicSDKAdapter(apiKey, cfg.AgentModel, edgehttp.NewClient(sdk.AnthropicHTTPTimeout)) if err := reg.Register(a); err != nil { slog.Warn("failed to register anthropic-sdk adapter", "err", err) } else { @@ -155,7 +156,7 @@ func registerSDKAdapters(reg *adapters.Registry, cfg config) { } if cfg.OpenAISDKPath != "" { apiKey := resolveSDKAPIKey(cfg.OpenAISDKPath, "OPENAI_API_KEY") - a := adapters.NewOpenAISDKAdapter(apiKey, cfg.AgentModel, edgehttp.NewClient(adapters.OpenAIHTTPTimeout)) + a := sdk.NewOpenAISDKAdapter(apiKey, cfg.AgentModel, edgehttp.NewClient(sdk.OpenAIHTTPTimeout)) if err := reg.Register(a); err != nil { slog.Warn("failed to register openai-sdk adapter", "err", err) } else { diff --git a/edge-server/internal/adapters/adapter_latency_test.go b/edge-server/internal/adapters/adapter_latency_test.go index c5aa6f9c2..84645d4ee 100644 --- a/edge-server/internal/adapters/adapter_latency_test.go +++ b/edge-server/internal/adapters/adapter_latency_test.go @@ -1,96 +1,15 @@ package adapters import ( - "net/http" "strings" "testing" - "time" "github.com/agenthub/edge-server/internal/router" - "github.com/agenthub/edge-server/internal/runnerctx" ) -// ============================================================================= -// Benchmark: SDK adapter creation + request building latency -// ============================================================================= - -// BenchmarkSDKAdapterLatency measures the wall-clock time for creating -// an AnthropicSDKAdapter and an OpenAISDKAdapter, calling BuildCommand, -// and building the outgoing messages array (buildMessages). These are the -// hot-path operations that run on every agent invocation before any HTTP -// request is made. -func BenchmarkSDKAdapterLatency(b *testing.B) { - runCtx := runnerctx.RunProcessContext{ - SystemPrompt: "You are a helpful assistant.", - Prompt: "What is the capital of France?", - Model: "claude-sonnet-4-6", - } - - b.Run("anthropic_sdk", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - adapter := NewAnthropicSDKAdapter("test-key-not-used", "claude-sonnet-4-6", &http.Client{}) - adapter.BuildCommand(runCtx) - _ = adapter.buildMessages(runCtx) - } - }) - - b.Run("openai_sdk", func(b *testing.B) { - b.ReportAllocs() - for i := 0; i < b.N; i++ { - adapter := NewOpenAISDKAdapter("test-key-not-used", "gpt-5.5", &http.Client{}) - adapter.BuildCommand(runCtx) - _ = adapter.buildMessages(runCtx) - } - }) -} - -// ============================================================================= -// Test: adapter setup latency baseline (<100ms) -// ============================================================================= - -// TestSDKAdapterLatencyBaseline asserts that adapter creation, BuildCommand, -// and buildMessages complete in under 100ms. This is a generous baseline — -// typical times are well under 1ms. The test acts as a canary for accidental -// regressions (e.g. blocking I/O, expensive init, unbounded allocations). -func TestSDKAdapterLatencyBaseline(t *testing.T) { - runCtx := runnerctx.RunProcessContext{ - SystemPrompt: "You are a helpful assistant.", - Prompt: "What is the capital of France?", - Model: "claude-sonnet-4-6", - } - - const maxLatency = 100 * time.Millisecond - - t.Run("anthropic_sdk", func(t *testing.T) { - start := time.Now() - adapter := NewAnthropicSDKAdapter("test-key-not-used", "claude-sonnet-4-6", &http.Client{}) - adapter.BuildCommand(runCtx) - _ = adapter.buildMessages(runCtx) - elapsed := time.Since(start) - - if elapsed > maxLatency { - t.Errorf("AnthropicSDK adapter setup took %v, want <= %v", elapsed, maxLatency) - } - t.Logf("AnthropicSDK setup: %v", elapsed) - }) - - t.Run("openai_sdk", func(t *testing.T) { - start := time.Now() - adapter := NewOpenAISDKAdapter("test-key-not-used", "gpt-5.5", &http.Client{}) - adapter.BuildCommand(runCtx) - _ = adapter.buildMessages(runCtx) - elapsed := time.Since(start) - - if elapsed > maxLatency { - t.Errorf("OpenAISDK adapter setup took %v, want <= %v", elapsed, maxLatency) - } - t.Logf("OpenAISDK setup: %v", elapsed) - }) -} - // ============================================================================= // Benchmark: ClassifyComplexity with various prompt sizes +// (SDK 适配器延迟基准已随 #1760 归组迁入 internal/adapters/sdk) // ============================================================================= // BenchmarkClassifyComplexity benchmarks the deterministic prompt complexity diff --git a/edge-server/internal/adapters/sdk/adapter_latency_test.go b/edge-server/internal/adapters/sdk/adapter_latency_test.go new file mode 100644 index 000000000..72e5eea56 --- /dev/null +++ b/edge-server/internal/adapters/sdk/adapter_latency_test.go @@ -0,0 +1,89 @@ +package sdk + +import ( + "net/http" + "testing" + "time" + + "github.com/agenthub/edge-server/internal/runnerctx" +) + +// ============================================================================= +// Benchmark: SDK adapter creation + request building latency +// (随 anthropic/openai SDK 适配器归组迁入 package sdk,#1760) +// ============================================================================= + +// BenchmarkSDKAdapterLatency measures the wall-clock time for creating +// an AnthropicSDKAdapter and an OpenAISDKAdapter, calling BuildCommand, +// and building the outgoing messages array (buildMessages). These are the +// hot-path operations that run on every agent invocation before any HTTP +// request is made. +func BenchmarkSDKAdapterLatency(b *testing.B) { + runCtx := runnerctx.RunProcessContext{ + SystemPrompt: "You are a helpful assistant.", + Prompt: "What is the capital of France?", + Model: "claude-sonnet-4-6", + } + + b.Run("anthropic_sdk", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + adapter := NewAnthropicSDKAdapter("test-key-not-used", "claude-sonnet-4-6", &http.Client{}) + adapter.BuildCommand(runCtx) + _ = adapter.buildMessages(runCtx) + } + }) + + b.Run("openai_sdk", func(b *testing.B) { + b.ReportAllocs() + for i := 0; i < b.N; i++ { + adapter := NewOpenAISDKAdapter("test-key-not-used", "gpt-5.5", &http.Client{}) + adapter.BuildCommand(runCtx) + _ = adapter.buildMessages(runCtx) + } + }) +} + +// ============================================================================= +// Test: adapter setup latency baseline (<100ms) +// ============================================================================= + +// TestSDKAdapterLatencyBaseline asserts that adapter creation, BuildCommand, +// and buildMessages complete in under 100ms. This is a generous baseline — +// typical times are well under 1ms. The test acts as a canary for accidental +// regressions (e.g. blocking I/O, expensive init, unbounded allocations). +func TestSDKAdapterLatencyBaseline(t *testing.T) { + runCtx := runnerctx.RunProcessContext{ + SystemPrompt: "You are a helpful assistant.", + Prompt: "What is the capital of France?", + Model: "claude-sonnet-4-6", + } + + const maxLatency = 100 * time.Millisecond + + t.Run("anthropic_sdk", func(t *testing.T) { + start := time.Now() + adapter := NewAnthropicSDKAdapter("test-key-not-used", "claude-sonnet-4-6", &http.Client{}) + adapter.BuildCommand(runCtx) + _ = adapter.buildMessages(runCtx) + elapsed := time.Since(start) + + if elapsed > maxLatency { + t.Errorf("AnthropicSDK adapter setup took %v, want <= %v", elapsed, maxLatency) + } + t.Logf("AnthropicSDK setup: %v", elapsed) + }) + + t.Run("openai_sdk", func(t *testing.T) { + start := time.Now() + adapter := NewOpenAISDKAdapter("test-key-not-used", "gpt-5.5", &http.Client{}) + adapter.BuildCommand(runCtx) + _ = adapter.buildMessages(runCtx) + elapsed := time.Since(start) + + if elapsed > maxLatency { + t.Errorf("OpenAISDK adapter setup took %v, want <= %v", elapsed, maxLatency) + } + t.Logf("OpenAISDK setup: %v", elapsed) + }) +} diff --git a/edge-server/internal/adapters/sdk/aliases.go b/edge-server/internal/adapters/sdk/aliases.go new file mode 100644 index 000000000..b6a39232f --- /dev/null +++ b/edge-server/internal/adapters/sdk/aliases.go @@ -0,0 +1,29 @@ +// Contract aliases for package sdk(#1760 首个增量)。 +// +// 合同类型与 BusEvent* 常量的唯一权威(SSOT)在 internal/orchestration; +// 本文件与 adapters 根包的 contract_aliases.go、兄弟叶子包 +// adapters/orchestrator 的 aliases.go 采用同一模式:派生 alias / 常量, +// 不复制定义,使归组后的实现文件继续免限定引用合同词汇。 +package sdk + +import "github.com/agenthub/edge-server/internal/orchestration" + +// ── Contract type aliases (single SSOT: internal/orchestration) ───────────── + +type EventEmitter = orchestration.EventEmitter +type AdapterMetadata = orchestration.AdapterMetadata +type AgentCapabilities = orchestration.AgentCapabilities +type RunProcessContext = orchestration.RunProcessContext + +// ── Bus event type strings(与 orchestration 完全一致,防双 SSOT)──────────── +const ( + BusEventAPIRetry = orchestration.BusEventAPIRetry + BusEventContextUsage = orchestration.BusEventContextUsage + BusEventResult = orchestration.BusEventResult + BusEventSessionInit = orchestration.BusEventSessionInit + BusEventStatusChange = orchestration.BusEventStatusChange + BusEventTextBlock = orchestration.BusEventTextBlock + BusEventTextDelta = orchestration.BusEventTextDelta + BusEventThinking = orchestration.BusEventThinking + BusEventToolCall = orchestration.BusEventToolCall +) diff --git a/edge-server/internal/adapters/anthropic_sdk.go b/edge-server/internal/adapters/sdk/anthropic_sdk.go similarity index 94% rename from edge-server/internal/adapters/anthropic_sdk.go rename to edge-server/internal/adapters/sdk/anthropic_sdk.go index 07563a6c9..62a074565 100644 --- a/edge-server/internal/adapters/anthropic_sdk.go +++ b/edge-server/internal/adapters/sdk/anthropic_sdk.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "context" @@ -10,11 +10,13 @@ import ( "strings" "time" + "github.com/agenthub/edge-server/internal/adapters" "github.com/agenthub/edge-server/internal/store" ) // Residual pure-helper peel #1142: keep AnthropicSDKAdapter surface + ParseStream -// orchestration; request/SSE/types moved to companion files. +// orchestration; request/SSE/types moved to companion files. Grouped into +// package sdk (#1760). const ( anthropicSDKAdapterID = "anthropic-sdk" @@ -176,13 +178,13 @@ func (a *AnthropicSDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, // Extract RunProcessContext from the context to get the prompt and model. var runCtx RunProcessContext - if rc, ok := ctx.Value(CtxRunContext).(RunProcessContext); ok { + if rc, ok := ctx.Value(adapters.CtxRunContext).(RunProcessContext); ok { runCtx = rc } model := a.model if runCtx.Model != "" { - model = ResolveModel(anthropicSDKAdapterID, runCtx.Model) + model = adapters.ResolveModel(anthropicSDKAdapterID, runCtx.Model) if model == "" { model = runCtx.Model } @@ -253,7 +255,7 @@ func (a *AnthropicSDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, bodyBytes, err := json.Marshal(requestBody) if err != nil { - return NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: failed to marshal request: %w", err)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: failed to marshal request: %w", err)) } // Emit session init @@ -279,7 +281,7 @@ func (a *AnthropicSDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, "terminalReason": "error", "provider": "anthropic", }) - return NewNonRecoverableParseError(fmt.Errorf("%s", errMsg)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("%s", errMsg)) } // Parse SSE stream diff --git a/edge-server/internal/adapters/anthropic_sdk_e2e_test.go b/edge-server/internal/adapters/sdk/anthropic_sdk_e2e_test.go similarity index 99% rename from edge-server/internal/adapters/anthropic_sdk_e2e_test.go rename to edge-server/internal/adapters/sdk/anthropic_sdk_e2e_test.go index 22f8eb527..49f5dd251 100644 --- a/edge-server/internal/adapters/anthropic_sdk_e2e_test.go +++ b/edge-server/internal/adapters/sdk/anthropic_sdk_e2e_test.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "context" diff --git a/edge-server/internal/adapters/anthropic_sdk_request.go b/edge-server/internal/adapters/sdk/anthropic_sdk_request.go similarity index 90% rename from edge-server/internal/adapters/anthropic_sdk_request.go rename to edge-server/internal/adapters/sdk/anthropic_sdk_request.go index 1e121896d..5e227f1ce 100644 --- a/edge-server/internal/adapters/anthropic_sdk_request.go +++ b/edge-server/internal/adapters/sdk/anthropic_sdk_request.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "bytes" @@ -10,11 +10,12 @@ import ( "net/http" "time" + "github.com/agenthub/edge-server/internal/adapters" "github.com/agenthub/edge-server/internal/runnerctx" ) // Residual pure-helper peel #1142: Anthropic SDK HTTP request + message build helpers. -// Same package adapters; doRequestWithRetry / buildMessages called from ParseStream. +// Grouped into package sdk (#1760); doRequestWithRetry / buildMessages called from ParseStream. // doRequestWithRetry makes the HTTP request with automatic retry for transient // failures (429 rate limit, 500/502/503/504 server errors). Auth errors (401/403) @@ -50,7 +51,7 @@ func (a *AnthropicSDKAdapter) doRequestWithRetry(ctx context.Context, body []byt req, err := http.NewRequestWithContext(ctx, http.MethodPost, a.baseURL+"/v1/messages", bytes.NewReader(body)) if err != nil { - return nil, NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: failed to create request: %w", err)) + return nil, adapters.NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: failed to create request: %w", err)) } req.Header.Set("Content-Type", "application/json") req.Header.Set("x-api-key", a.apiKey) @@ -90,7 +91,7 @@ func (a *AnthropicSDKAdapter) doRequestWithRetry(ctx context.Context, body []byt "terminalReason": "error", "provider": "anthropic", }) - return nil, NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: request failed after %d retries: %w", anthropicMaxRetries, lastErr)) + return nil, adapters.NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: request failed after %d retries: %w", anthropicMaxRetries, lastErr)) } // buildMessages converts the RunProcessContext into Anthropic message format. diff --git a/edge-server/internal/adapters/anthropic_sdk_sse.go b/edge-server/internal/adapters/sdk/anthropic_sdk_sse.go similarity index 94% rename from edge-server/internal/adapters/anthropic_sdk_sse.go rename to edge-server/internal/adapters/sdk/anthropic_sdk_sse.go index 5f2d6a118..58f8e49f4 100644 --- a/edge-server/internal/adapters/anthropic_sdk_sse.go +++ b/edge-server/internal/adapters/sdk/anthropic_sdk_sse.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "bufio" @@ -8,10 +8,12 @@ import ( "io" "log/slog" "strings" + + "github.com/agenthub/edge-server/internal/adapters" ) // Residual pure-helper peel #1142: Anthropic SSE stream parse + event dispatch. -// Same package adapters; ParseStream continues to call parseSSEStream. +// Grouped into package sdk (#1760); ParseStream continues to call parseSSEStream. // anthropicSSEState accumulates cross-event state while parsing an SSE stream. type anthropicSSEState struct { @@ -69,7 +71,7 @@ func (a *AnthropicSDKAdapter) parseSSEStream(ctx context.Context, body io.Reader if ctx.Err() != nil { return ctx.Err() } - return NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: SSE stream read error: %w", err)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: SSE stream read error: %w", err)) } return nil @@ -232,5 +234,5 @@ func handleAnthropicStreamError(event anthropicSSEEvent, emitter EventEmitter, s "terminalReason": "error", "provider": "anthropic", }) - return NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: API error: %s", errMsg)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("anthropic-sdk: API error: %s", errMsg)) } diff --git a/edge-server/internal/adapters/anthropic_sdk_types.go b/edge-server/internal/adapters/sdk/anthropic_sdk_types.go similarity index 96% rename from edge-server/internal/adapters/anthropic_sdk_types.go rename to edge-server/internal/adapters/sdk/anthropic_sdk_types.go index fc3d41fc6..a97f18193 100644 --- a/edge-server/internal/adapters/anthropic_sdk_types.go +++ b/edge-server/internal/adapters/sdk/anthropic_sdk_types.go @@ -1,7 +1,7 @@ -package adapters +package sdk // Residual pure-helper peel #1142: Anthropic Messages API request/SSE types. -// Same package adapters; public Adapter surface unchanged. +// Grouped into package sdk (#1760); public Adapter surface unchanged. // --- Anthropic API types --- diff --git a/edge-server/internal/adapters/sdk/doc.go b/edge-server/internal/adapters/sdk/doc.go new file mode 100644 index 000000000..db7c209f8 --- /dev/null +++ b/edge-server/internal/adapters/sdk/doc.go @@ -0,0 +1,26 @@ +// Package sdk 收纳 AgentHub 的直连 HTTP「SDK 适配器」(anthropic-sdk 与 +// openai-sdk):不经过 CLI 子进程,BuildCommand 返回哨兵命令,ParseStream +// 直接发起对提供商 API 的流式 HTTP 调用并映射为 Edge 事件。 +// +// 本包是 #1760「adapters 归组以降低认知负载」的首个安全增量:从根 +// internal/adapters 的 80+ 个平铺文件中,把内聚的 SDK 适配器集合 +// (anthropic_sdk*.go / openai_sdk*.go / sdk_common*.go 及其测试) +// 下沉为子包。 +// +// 依赖方向(单向,无环): +// +// internal/adapters/sdk → internal/adapters(ResolveModel、CtxRunContext、 +// NewNonRecoverableParseError 等仍留在根包的助手) +// internal/adapters/sdk → internal/orchestration(合同类型 SSOT,经 +// aliases.go 引入,模式对齐 adapters/orchestrator 叶子包) +// cmd/agenthub-edge(组合根)→ internal/adapters/sdk +// +// 根 internal/adapters 不 import 本子包:根包对 SDK 适配器的唯一残留引用 +// 是 registry.go 中的字符串适配器 ID("anthropic-sdk" / "openai-sdk"), +// 注册由组合根完成,因此方向天然单向。 +// +// 仍留在根包、暂未归组的 sdk_fixture_mapper*.go 与根包存在双向耦合 +// (mapper 使用根包 BusEvent* 常量;根包 agentspec_fixture.go 构造 +// SDKFixture* 类型并调用其它适配器构造函数),强行下沉会形成 import +// cycle,留待后续增量解耦。 +package sdk diff --git a/edge-server/internal/adapters/openai_sdk.go b/edge-server/internal/adapters/sdk/openai_sdk.go similarity index 92% rename from edge-server/internal/adapters/openai_sdk.go rename to edge-server/internal/adapters/sdk/openai_sdk.go index 79ecc992a..e26294e70 100644 --- a/edge-server/internal/adapters/openai_sdk.go +++ b/edge-server/internal/adapters/sdk/openai_sdk.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "context" @@ -10,11 +10,13 @@ import ( "strings" "time" + "github.com/agenthub/edge-server/internal/adapters" "github.com/agenthub/edge-server/internal/store" ) // Residual pure-helper peel #1152: keep OpenAISDKAdapter surface + ParseStream -// orchestration; request/SSE/types moved to companion files. +// orchestration; request/SSE/types moved to companion files. Grouped into +// package sdk (#1760). const ( openaiSDKAdapterID = "openai-sdk" @@ -169,13 +171,13 @@ func (a *OpenAISDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, st // Extract RunProcessContext from the context to get the prompt and model. var runCtx RunProcessContext - if rc, ok := ctx.Value(CtxRunContext).(RunProcessContext); ok { + if rc, ok := ctx.Value(adapters.CtxRunContext).(RunProcessContext); ok { runCtx = rc } model := a.model if runCtx.Model != "" { - model = ResolveModel(openaiSDKAdapterID, runCtx.Model) + model = adapters.ResolveModel(openaiSDKAdapterID, runCtx.Model) if model == "" { model = runCtx.Model } @@ -196,7 +198,7 @@ func (a *OpenAISDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, st // Reasoning effort if runCtx.ReasoningEffort != "" { effort := runCtx.ReasoningEffort - if resolved := ResolveReasoningEffort(openaiSDKAdapterID, runCtx.ReasoningEffort); resolved != "" { + if resolved := adapters.ResolveReasoningEffort(openaiSDKAdapterID, runCtx.ReasoningEffort); resolved != "" { effort = resolved } requestBody.ReasoningEffort = effort @@ -212,7 +214,7 @@ func (a *OpenAISDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, st bodyBytes, err := json.Marshal(requestBody) if err != nil { - return NewNonRecoverableParseError(fmt.Errorf("openai-sdk: failed to marshal request: %w", err)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("openai-sdk: failed to marshal request: %w", err)) } // Emit session init @@ -238,7 +240,7 @@ func (a *OpenAISDKAdapter) ParseStream(ctx context.Context, stdout io.Reader, st "terminalReason": "error", "provider": "openai", }) - return NewNonRecoverableParseError(fmt.Errorf("%s", errMsg)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("%s", errMsg)) } // Parse SSE stream diff --git a/edge-server/internal/adapters/openai_sdk_e2e_test.go b/edge-server/internal/adapters/sdk/openai_sdk_e2e_test.go similarity index 99% rename from edge-server/internal/adapters/openai_sdk_e2e_test.go rename to edge-server/internal/adapters/sdk/openai_sdk_e2e_test.go index f4724a6bd..0a3632144 100644 --- a/edge-server/internal/adapters/openai_sdk_e2e_test.go +++ b/edge-server/internal/adapters/sdk/openai_sdk_e2e_test.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "context" @@ -6,6 +6,7 @@ import ( "net/http/httptest" "testing" + "github.com/agenthub/edge-server/internal/adapters" "github.com/agenthub/edge-server/internal/runnerctx" "github.com/agenthub/edge-server/internal/store" ) @@ -1003,7 +1004,7 @@ func TestOpenAISDK_E2E_ParseStream_HTTPError(t *testing.T) { Prompt: "Hello", } - ctx := SDKAdapterContext(context.Background(), runCtx) + ctx := adapters.SDKAdapterContext(context.Background(), runCtx) err := adapter.ParseStream(ctx, nil, nil, emitter, run) if err == nil { diff --git a/edge-server/internal/adapters/openai_sdk_request.go b/edge-server/internal/adapters/sdk/openai_sdk_request.go similarity index 92% rename from edge-server/internal/adapters/openai_sdk_request.go rename to edge-server/internal/adapters/sdk/openai_sdk_request.go index bbf35bc43..9d812d4fe 100644 --- a/edge-server/internal/adapters/openai_sdk_request.go +++ b/edge-server/internal/adapters/sdk/openai_sdk_request.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "bytes" @@ -11,11 +11,12 @@ import ( "strings" "time" + "github.com/agenthub/edge-server/internal/adapters" "github.com/agenthub/edge-server/internal/runnerctx" ) // Residual pure-helper peel #1152: OpenAI SDK HTTP request + message build helpers. -// Same package adapters; doRequestWithRetry / buildMessages called from ParseStream. +// Grouped into package sdk (#1760); doRequestWithRetry / buildMessages called from ParseStream. // doRequestWithRetry makes the HTTP request with automatic retry for transient // failures (429 rate limit, 500/502/503/504 server errors). Auth errors (401/403) @@ -62,7 +63,7 @@ func (a *OpenAISDKAdapter) doRequestWithRetry(ctx context.Context, body []byte, req, err := http.NewRequestWithContext(ctx, http.MethodPost, a.baseURL+"/v1/chat/completions", bytes.NewReader(body)) if err != nil { - return nil, NewNonRecoverableParseError(fmt.Errorf("openai-sdk: failed to create request: %w", err)) + return nil, adapters.NewNonRecoverableParseError(fmt.Errorf("openai-sdk: failed to create request: %w", err)) } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+a.apiKey) @@ -101,7 +102,7 @@ func (a *OpenAISDKAdapter) doRequestWithRetry(ctx context.Context, body []byte, "terminalReason": "error", "provider": "openai", }) - return nil, NewNonRecoverableParseError(fmt.Errorf("openai-sdk: request failed after %d retries: %w", openaiMaxRetries, lastErr)) + return nil, adapters.NewNonRecoverableParseError(fmt.Errorf("openai-sdk: request failed after %d retries: %w", openaiMaxRetries, lastErr)) } // buildMessages converts the RunProcessContext into OpenAI message format. diff --git a/edge-server/internal/adapters/openai_sdk_sse.go b/edge-server/internal/adapters/sdk/openai_sdk_sse.go similarity index 95% rename from edge-server/internal/adapters/openai_sdk_sse.go rename to edge-server/internal/adapters/sdk/openai_sdk_sse.go index 77355e519..0dc4307e4 100644 --- a/edge-server/internal/adapters/openai_sdk_sse.go +++ b/edge-server/internal/adapters/sdk/openai_sdk_sse.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "bufio" @@ -8,10 +8,12 @@ import ( "io" "log/slog" "strings" + + "github.com/agenthub/edge-server/internal/adapters" ) // Residual pure-helper peel #1152: OpenAI SSE stream parse + event dispatch. -// Same package adapters; ParseStream continues to call parseSSEStream. +// Grouped into package sdk (#1760); ParseStream continues to call parseSSEStream. // openaiSSEState accumulates cross-chunk state while parsing an SSE stream. type openaiSSEState struct { @@ -65,7 +67,7 @@ func (a *OpenAISDKAdapter) parseSSEStream(ctx context.Context, body io.Reader, e if ctx.Err() != nil { return ctx.Err() } - return NewNonRecoverableParseError(fmt.Errorf("openai-sdk: SSE stream read error: %w", err)) + return adapters.NewNonRecoverableParseError(fmt.Errorf("openai-sdk: SSE stream read error: %w", err)) } emitOpenAIFinalEvents(emitter, scope, model, st) diff --git a/edge-server/internal/adapters/openai_sdk_types.go b/edge-server/internal/adapters/sdk/openai_sdk_types.go similarity index 96% rename from edge-server/internal/adapters/openai_sdk_types.go rename to edge-server/internal/adapters/sdk/openai_sdk_types.go index 840aa9836..d164d3bc7 100644 --- a/edge-server/internal/adapters/openai_sdk_types.go +++ b/edge-server/internal/adapters/sdk/openai_sdk_types.go @@ -1,9 +1,9 @@ -package adapters +package sdk import "strings" // Residual pure-helper peel #1152: OpenAI Chat Completions API request/SSE types. -// Same package adapters; public Adapter surface unchanged. +// Grouped into package sdk (#1760); public Adapter surface unchanged. // --- OpenAI API types --- diff --git a/edge-server/internal/adapters/sdk_common.go b/edge-server/internal/adapters/sdk/sdk_common.go similarity index 96% rename from edge-server/internal/adapters/sdk_common.go rename to edge-server/internal/adapters/sdk/sdk_common.go index ee0ce55c9..aa67e91b4 100644 --- a/edge-server/internal/adapters/sdk_common.go +++ b/edge-server/internal/adapters/sdk/sdk_common.go @@ -1,4 +1,4 @@ -package adapters +package sdk import "runtime" diff --git a/edge-server/internal/adapters/sdk_common_test.go b/edge-server/internal/adapters/sdk/sdk_common_test.go similarity index 98% rename from edge-server/internal/adapters/sdk_common_test.go rename to edge-server/internal/adapters/sdk/sdk_common_test.go index c406e278d..47d997d5b 100644 --- a/edge-server/internal/adapters/sdk_common_test.go +++ b/edge-server/internal/adapters/sdk/sdk_common_test.go @@ -1,4 +1,4 @@ -package adapters +package sdk import ( "runtime"