Skip to content

fix(agent): point glm.go and deepseek.go at Anthropic-compat proxies #4

Description

@wangke19

Problem

internal/agent/glm.go and internal/agent/deepseek.go implement the native protocols for their respective providers:

  • glm.go:66https://open.bigmodel.cn/api/paas/v4/ (BigModel native, chat/completions)
  • deepseek.go:77https://api.deepseek.com (DeepSeek native, /v1/chat/completions)

But the actual deployment serves both models through Anthropic Messages API proxies:

  • GLM → https://open.bigmodel.cn/api/anthropic (x-api-key auth, anthropic-version: 2023-06-01)
  • DeepSeek → https://token.sensenova.cn (Authorization: Bearer, Sensenova-hosted)

These are separate billing/auth paths from the native endpoints.

Evidence

Discovered while running the self-development loop for #1:

  • Calling paas/v4/chat/completions with the GLM token returned 429 余额不足 — even though the same token works fine on the Anthropic-compat endpoint (used by the CLI itself).
  • Calling api.deepseek.com returned 401 — the DeepSeek key in use is actually a Sensenova token, valid only at token.sensenova.cn.

cmd/selfdev/main.go (landed in #3) demonstrates the working request shape for both proxies — same auth token, two different header conventions, Anthropic Messages request/response bodies.

Impact

The production conductor binary cannot run a real agent loop end-to-end. Planner/executor/reviewer calls will fail at the HTTP layer on day one in any environment that uses the proxy-based deployment.

Proposed fix

Rewrite glm.go and deepseek.go to call the Anthropic Messages API through their respective proxies:

  1. Change default baseURL values:
    • glm.go: https://open.bigmodel.cn/api/anthropic
    • deepseek.go: https://token.sensenova.cn
  2. Request shape: POST {baseURL}/v1/messages with body {model, max_tokens, messages: [{role, content}]}.
  3. Headers:
    • Both: anthropic-version: 2023-06-01, Content-Type: application/json
    • GLM: x-api-key: <token>
    • DeepSeek: Authorization: Bearer <token> (set both — Sensenova specifically wants Bearer)
  4. Response shape: {content: [{type, text}], usage: {input_tokens, output_tokens}, stop_reason}.
  5. Drop the choices[0].message parsing path; map content[0].text into the existing Response.Text.

Consider extracting a shared anthropicClient since both providers speak the same protocol — only the baseURL and auth header differ.

Test strategy

  • httptest mock that returns a Messages-format response; assert the agent extracts content[0].text correctly.
  • Error mapping: 401/403 → blocked, 429 → retryable, 5xx → retryable.
  • Keep the existing BaseURL config override so tests still work.

Reference

  • Working harness: cmd/selfdev/main.go (already committed)
  • Artifacts: self-dev-artifacts/issue-1-planner-glm52.txt, self-dev-artifacts/issue-1-reviewer-deepseek.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    conductor-readyIssue ready for agents_conductor autonomous development

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions