Skip to content

fix(sdk): use async /v1/graphs/supermodel endpoint with polling#79

Merged
greynewell merged 2 commits intomainfrom
fix-sdk-async-endpoint
Apr 8, 2026
Merged

fix(sdk): use async /v1/graphs/supermodel endpoint with polling#79
greynewell merged 2 commits intomainfrom
fix-sdk-async-endpoint

Conversation

@greynewell
Copy link
Copy Markdown
Contributor

@greynewell greynewell commented Apr 8, 2026

Summary

  • pkg/supermodel AnalyzeZip was calling the legacy /v1/supermodel endpoint which returned a synchronous JSON response
  • All other code (internal client, scripts/check-architecture) uses /v1/graphs/supermodel which returns an async job envelope
  • This would cause the public SDK to fail or misparse responses since the response shapes don't match

Changes

  • Switch endpoint to /v1/graphs/supermodel
  • Add jobResponse/jobResult types to decode the async envelope (status, jobId, retryAfter, error, result)
  • Add polling loop with RetryAfter-based backoff (defaults to 5s) until job reaches terminal state
  • Extract Graph from result.graph on completion

Test plan

  • go build ./... passes
  • go test ./... passes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Analysis now runs asynchronously with automatic polling until completion, using retry intervals (fallback to 5s).
  • Bug Fixes

    • Improved error reporting for job failures and unexpected job statuses.
    • Clearer decoding error messages for job responses and analysis results.

AnalyzeZip was posting to the legacy /v1/supermodel endpoint which
returned a sync JSON response. All other code (internal client,
scripts/check-architecture) uses the current /v1/graphs/supermodel
endpoint that returns an async job envelope.

This change:
- Switches to /v1/graphs/supermodel
- Adds jobResponse/jobResult types to decode the async envelope
- Adds a polling loop that waits RetryAfter seconds between polls
  (defaulting to 5s) until the job reaches a terminal state
- Extracts the Graph from result.graph on completion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 75a88acd-97a5-4990-bfcd-32be3af37f84

📥 Commits

Reviewing files that changed from the base of the PR and between a989b34 and 0847b17.

📒 Files selected for processing (1)
  • pkg/supermodel/client.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/supermodel/client.go

Walkthrough

AnalyzeZip was changed from a direct synchronous POST/decode to an asynchronous job flow: it POSTs the ZIP to /v1/graphs/supermodel, receives a job envelope, polls the job until status is completed (respecting retryAfter with 5s fallback), then decodes the final graph result.

Changes

Cohort / File(s) Summary
Async Job Flow Refactor
pkg/supermodel/client.go
Replaced direct response decode with async job envelope handling: added jobResponse/jobResult types, postZip helper, polling loop honoring retryAfter (fallback 5s), updated endpoint to /v1/graphs/supermodel, and refined error messages for job failures and decode steps.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant API as Server
  participant JobSvc as JobService

  Client->>API: POST /v1/graphs/supermodel (zip, idempotency)
  API->>Client: 202 { jobId, status: "pending"|"processing", retryAfter }
  loop while status != completed
    Client->>API: GET /jobs/{jobId}
    API->>Client: { status, retryAfter, (error|result) }
    alt status is pending/processing
      Client->>Client: wait retryAfter (or 5s)
    end
  end
  alt status == completed
    API->>Client: { result: { graph, repo } }
    Client->>Client: decode graph result
  else unexpected status or error
    API->>Client: { error }
    Client->>Client: return analysis failed / unexpected status
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

Zip sent on a tiny quest,
JobId clutched within its chest,
Polling ticks and retry waits,
Completed brings the graph of fate,
Async hums — the ZIP’s impressed. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: switching to an async endpoint with polling instead of the legacy synchronous endpoint.
Description check ✅ Passed The description covers the key sections: what was changed (endpoint switch, new types, polling logic), why it was needed (response shape mismatch), and test results, though it deviates from the template structure.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-sdk-async-endpoint

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
pkg/supermodel/client.go (1)

228-245: Quick note on file re-reading during polls

Each time postZip is called (which happens on every poll iteration), you're:

  1. Opening the ZIP file
  2. Reading it into a buffer
  3. Re-uploading to the server

The server handles this correctly via the idempotency key (it'll just return the existing job status), so this is functionally correct. But for big repos, you're doing a lot of redundant I/O and network transfer.

The internal API client does the same thing, so this is consistent. Just something to be aware of if someone reports slow polling on large repos later - a future optimization could cache the job ID and poll a separate status endpoint (if the API supports it).

Not blocking since it matches internal behavior. 👌

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/supermodel/client.go` around lines 228 - 245, postZip currently re-opens
and re-uploads the ZIP on every poll which causes redundant I/O/network for
large repos; modify Client.postZip to first check a concurrency-safe cache
(e.g., Client-level map or sync.Map protected by a mutex) keyed by
idempotencyKey and return the cached *jobResponse if present, and only
open/read/upload the file when there is no cached entry; ensure the cache is
populated with the jobResponse after a successful upload and that the cache
access uses proper synchronization to avoid races.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@pkg/supermodel/client.go`:
- Around line 228-245: postZip currently re-opens and re-uploads the ZIP on
every poll which causes redundant I/O/network for large repos; modify
Client.postZip to first check a concurrency-safe cache (e.g., Client-level map
or sync.Map protected by a mutex) keyed by idempotencyKey and return the cached
*jobResponse if present, and only open/read/upload the file when there is no
cached entry; ensure the cache is populated with the jobResponse after a
successful upload and that the cache access uses proper synchronization to avoid
races.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ab416a5d-2a56-4468-9de2-4191294ba124

📥 Commits

Reviewing files that changed from the base of the PR and between 265c729 and a989b34.

📒 Files selected for processing (1)
  • pkg/supermodel/client.go

The /v1/graphs/supermodel result includes a top-level "repo" field
alongside "graph". jobResult now captures it, and AnalyzeZip sets
graph.Metadata["repoId"] so Graph.RepoID() works correctly.

Without this, Graph.RepoID() always returned "" for SDK callers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greynewell greynewell merged commit b81efe1 into main Apr 8, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant