|
| 1 | +--- |
| 2 | +title: 'Async Polling' |
| 3 | +description: 'How graph generation jobs work and how to poll for results' |
| 4 | +icon: 'arrows-rotate' |
| 5 | +--- |
| 6 | + |
| 7 | +All graph generation endpoints are **asynchronous**. When you submit a request, the API creates a background job and returns immediately with a job status. You then poll for results by re-submitting the same request with the same `Idempotency-Key`. |
| 8 | + |
| 9 | +## How It Works |
| 10 | + |
| 11 | +```mermaid |
| 12 | +sequenceDiagram |
| 13 | + participant Client |
| 14 | + participant API |
| 15 | +
|
| 16 | + Client->>API: POST /v1/graphs/dependency (file + Idempotency-Key) |
| 17 | + API-->>Client: 202 Accepted {status: "pending", retryAfter: 10} |
| 18 | + Note over Client: Wait retryAfter seconds |
| 19 | + Client->>API: POST /v1/graphs/dependency (same file + same key) |
| 20 | + API-->>Client: 202 Accepted {status: "processing", retryAfter: 10} |
| 21 | + Note over Client: Wait retryAfter seconds |
| 22 | + Client->>API: POST /v1/graphs/dependency (same file + same key) |
| 23 | + API-->>Client: 200 OK {status: "completed", result: {graph: {...}}} |
| 24 | +``` |
| 25 | + |
| 26 | +## Job Statuses |
| 27 | + |
| 28 | +| Status | HTTP Code | Description | |
| 29 | +|--------|-----------|-------------| |
| 30 | +| `pending` | 202 | Job is queued, waiting to be processed | |
| 31 | +| `processing` | 202 | Job is actively being analyzed | |
| 32 | +| `completed` | 200 | Job finished successfully, `result` field contains the graph | |
| 33 | +| `failed` | 200 | Job encountered an error, `error` field contains the message | |
| 34 | + |
| 35 | +## Response Envelope |
| 36 | + |
| 37 | +All graph endpoints return a consistent envelope: |
| 38 | + |
| 39 | +```json |
| 40 | +{ |
| 41 | + "status": "pending | processing | completed | failed", |
| 42 | + "jobId": "unique-job-identifier", |
| 43 | + "retryAfter": 10, |
| 44 | + "result": { ... }, |
| 45 | + "error": "error message if failed" |
| 46 | +} |
| 47 | +``` |
| 48 | + |
| 49 | +- **`status`** - Current job state |
| 50 | +- **`jobId`** - Unique identifier for the job |
| 51 | +- **`retryAfter`** - Recommended seconds to wait before the next poll (only present for pending/processing) |
| 52 | +- **`result`** - The graph data (only present when completed) |
| 53 | +- **`error`** - Error description (only present when failed) |
| 54 | + |
| 55 | +## Polling with cURL |
| 56 | + |
| 57 | +Store the `Idempotency-Key` in a variable and re-use it for polling: |
| 58 | + |
| 59 | +```bash |
| 60 | +IDEMPOTENCY_KEY=$(uuidgen) |
| 61 | + |
| 62 | +# Submit the job |
| 63 | +curl --request POST \ |
| 64 | + --url https://api.supermodeltools.com/v1/graphs/dependency \ |
| 65 | + --header "Idempotency-Key: $IDEMPOTENCY_KEY" \ |
| 66 | + --header 'X-Api-Key: <your-api-key>' \ |
| 67 | + --header 'Content-Type: multipart/form-data' \ |
| 68 | + --form file='@repo.zip' |
| 69 | + |
| 70 | +# Poll until completed (re-submit the same request) |
| 71 | +curl --request POST \ |
| 72 | + --url https://api.supermodeltools.com/v1/graphs/dependency \ |
| 73 | + --header "Idempotency-Key: $IDEMPOTENCY_KEY" \ |
| 74 | + --header 'X-Api-Key: <your-api-key>' \ |
| 75 | + --header 'Content-Type: multipart/form-data' \ |
| 76 | + --form file='@repo.zip' |
| 77 | +``` |
| 78 | + |
| 79 | +<Tip> |
| 80 | + The server uses the `Idempotency-Key` to identify your existing job. Re-submitting the file does not create a duplicate job. |
| 81 | +</Tip> |
| 82 | + |
| 83 | +## Using the SDK |
| 84 | + |
| 85 | +The `@supermodeltools/sdk` package handles polling automatically. Install it with: |
| 86 | + |
| 87 | +```bash |
| 88 | +npm install @supermodeltools/sdk |
| 89 | +``` |
| 90 | + |
| 91 | +### Basic Usage |
| 92 | + |
| 93 | +```typescript |
| 94 | +import { Configuration, DefaultApi, SupermodelClient } from '@supermodeltools/sdk'; |
| 95 | +import * as fs from 'fs'; |
| 96 | + |
| 97 | +const config = new Configuration({ |
| 98 | + basePath: 'https://api.supermodeltools.com', |
| 99 | + apiKey: 'smsk_live_...', |
| 100 | +}); |
| 101 | + |
| 102 | +const api = new DefaultApi(config); |
| 103 | +const client = new SupermodelClient(api); |
| 104 | + |
| 105 | +// Read your zip file |
| 106 | +const zipBuffer = fs.readFileSync('repo.zip'); |
| 107 | +const file = new Blob([zipBuffer], { type: 'application/zip' }); |
| 108 | + |
| 109 | +// This handles polling internally and returns the completed result |
| 110 | +const result = await client.generateDependencyGraph(file); |
| 111 | +console.log(result.graph.nodes); |
| 112 | +``` |
| 113 | + |
| 114 | +### Configuring Polling Behavior |
| 115 | + |
| 116 | +```typescript |
| 117 | +const controller = new AbortController(); |
| 118 | + |
| 119 | +const client = new SupermodelClient(api, { |
| 120 | + timeoutMs: 600000, // Max wait time: 10 minutes (default: 5 minutes) |
| 121 | + defaultRetryIntervalMs: 3000, // Poll interval if server doesn't specify (default: 5s) |
| 122 | + maxPollingAttempts: 120, // Max number of polls (default: 60) |
| 123 | + onPollingProgress: (progress) => { |
| 124 | + console.log(`Attempt ${progress.attempt}/${progress.maxAttempts} - ${progress.status}`); |
| 125 | + }, |
| 126 | + signal: controller.signal, // AbortSignal for cancellation |
| 127 | +}); |
| 128 | + |
| 129 | +// To cancel polling at any point: |
| 130 | +// controller.abort(); |
| 131 | +``` |
| 132 | + |
| 133 | +### Available Methods |
| 134 | + |
| 135 | +| Method | Endpoint | |
| 136 | +|--------|----------| |
| 137 | +| `client.generateDependencyGraph(file)` | `/v1/graphs/dependency` | |
| 138 | +| `client.generateCallGraph(file)` | `/v1/graphs/call` | |
| 139 | +| `client.generateDomainGraph(file)` | `/v1/graphs/domain` | |
| 140 | +| `client.generateParseGraph(file)` | `/v1/graphs/parse` | |
| 141 | +| `client.generateSupermodelGraph(file)` | `/v1/graphs/supermodel` | |
| 142 | + |
| 143 | +## Error Handling |
| 144 | + |
| 145 | +If a job fails, the response will have `status: "failed"` with an `error` message: |
| 146 | + |
| 147 | +```json |
| 148 | +{ |
| 149 | + "status": "failed", |
| 150 | + "jobId": "550e8400-e29b-41d4-a716-446655440000", |
| 151 | + "error": "Nested archives are not supported" |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +Common failure reasons: |
| 156 | + |
| 157 | +| Error | Resolution | |
| 158 | +|-------|------------| |
| 159 | +| Nested archives | Exclude `.zip`/`.tar` files from your archive using `.gitattributes` with `export-ignore` | |
| 160 | +| File exceeds size limits | Exclude large binary files from the archive | |
| 161 | +| Blob expired | Job waited too long in the queue; retry with a new idempotency key | |
| 162 | + |
| 163 | +<Warning> |
| 164 | + Jobs have a limited processing window. If a job stays in `pending` status too long, the uploaded file may expire and the job will be marked as `failed`. |
| 165 | +</Warning> |
| 166 | + |
| 167 | +## Idempotency Key Behavior |
| 168 | + |
| 169 | +The `Idempotency-Key` scopes a job to your API key. Key behaviors: |
| 170 | + |
| 171 | +- **Same key, same user**: Returns the existing job (no duplicate processing) |
| 172 | +- **Same key, different user**: Creates independent jobs (no conflict) |
| 173 | +- **New key, same file**: Creates a new job (useful for re-analysis after code changes) |
| 174 | + |
| 175 | +<Info> |
| 176 | + Completed jobs are retained for 24 hours. After that, submitting the same idempotency key will create a new job. |
| 177 | +</Info> |
0 commit comments