Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 177 additions & 0 deletions async-polling.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
title: 'Async Polling'
description: 'How graph generation jobs work and how to poll for results'
icon: 'arrows-rotate'
---

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`.

## How It Works

```mermaid
sequenceDiagram
participant Client
participant API

Client->>API: POST /v1/graphs/dependency (file + Idempotency-Key)
API-->>Client: 202 Accepted {status: "pending", retryAfter: 10}
Note over Client: Wait retryAfter seconds
Client->>API: POST /v1/graphs/dependency (same file + same key)
API-->>Client: 202 Accepted {status: "processing", retryAfter: 10}
Note over Client: Wait retryAfter seconds
Client->>API: POST /v1/graphs/dependency (same file + same key)
API-->>Client: 200 OK {status: "completed", result: {graph: {...}}}
```

## Job Statuses

| Status | HTTP Code | Description |
|--------|-----------|-------------|
| `pending` | 202 | Job is queued, waiting to be processed |
| `processing` | 202 | Job is actively being analyzed |
| `completed` | 200 | Job finished successfully, `result` field contains the graph |
| `failed` | 200 | Job encountered an error, `error` field contains the message |

## Response Envelope

All graph endpoints return a consistent envelope:

```json
{
"status": "pending | processing | completed | failed",
"jobId": "unique-job-identifier",
"retryAfter": 10,
"result": { ... },
"error": "error message if failed"
}
```

- **`status`** - Current job state
- **`jobId`** - Unique identifier for the job
- **`retryAfter`** - Recommended seconds to wait before the next poll (only present for pending/processing)
- **`result`** - The graph data (only present when completed)
- **`error`** - Error description (only present when failed)

## Polling with cURL

Store the `Idempotency-Key` in a variable and re-use it for polling:

```bash
IDEMPOTENCY_KEY=$(uuidgen)

# Submit the job
curl --request POST \
--url https://api.supermodeltools.com/v1/graphs/dependency \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--header 'X-Api-Key: <your-api-key>' \
--header 'Content-Type: multipart/form-data' \
--form file='@repo.zip'

# Poll until completed (re-submit the same request)
curl --request POST \
--url https://api.supermodeltools.com/v1/graphs/dependency \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--header 'X-Api-Key: <your-api-key>' \
--header 'Content-Type: multipart/form-data' \
--form file='@repo.zip'
```

<Tip>
The server uses the `Idempotency-Key` to identify your existing job. Re-submitting the file does not create a duplicate job.
</Tip>

## Using the SDK

The `@supermodeltools/sdk` package handles polling automatically. Install it with:

```bash
npm install @supermodeltools/sdk
```

### Basic Usage

```typescript
import { Configuration, DefaultApi, SupermodelClient } from '@supermodeltools/sdk';
import * as fs from 'fs';

const config = new Configuration({
basePath: 'https://api.supermodeltools.com',
apiKey: 'smsk_live_...',
});

const api = new DefaultApi(config);
const client = new SupermodelClient(api);

// Read your zip file
const zipBuffer = fs.readFileSync('repo.zip');
const file = new Blob([zipBuffer], { type: 'application/zip' });

// This handles polling internally and returns the completed result
const result = await client.generateDependencyGraph(file);
console.log(result.graph.nodes);
```

### Configuring Polling Behavior

```typescript
const controller = new AbortController();

const client = new SupermodelClient(api, {
timeoutMs: 600000, // Max wait time: 10 minutes (default: 5 minutes)
defaultRetryIntervalMs: 3000, // Poll interval if server doesn't specify (default: 5s)
maxPollingAttempts: 120, // Max number of polls (default: 60)
onPollingProgress: (progress) => {
console.log(`Attempt ${progress.attempt}/${progress.maxAttempts} - ${progress.status}`);
},
signal: controller.signal, // AbortSignal for cancellation
});

// To cancel polling at any point:
// controller.abort();
```

### Available Methods

| Method | Endpoint |
|--------|----------|
| `client.generateDependencyGraph(file)` | `/v1/graphs/dependency` |
| `client.generateCallGraph(file)` | `/v1/graphs/call` |
| `client.generateDomainGraph(file)` | `/v1/graphs/domain` |
| `client.generateParseGraph(file)` | `/v1/graphs/parse` |
| `client.generateSupermodelGraph(file)` | `/v1/graphs/supermodel` |

## Error Handling

If a job fails, the response will have `status: "failed"` with an `error` message:

```json
{
"status": "failed",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"error": "Nested archives are not supported"
}
```

Common failure reasons:

| Error | Resolution |
|-------|------------|
| Nested archives | Exclude `.zip`/`.tar` files from your archive using `.gitattributes` with `export-ignore` |
| File exceeds size limits | Exclude large binary files from the archive |
| Blob expired | Job waited too long in the queue; retry with a new idempotency key |

<Warning>
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`.
</Warning>

## Idempotency Key Behavior

The `Idempotency-Key` scopes a job to your API key. Key behaviors:

- **Same key, same user**: Returns the existing job (no duplicate processing)
- **Same key, different user**: Creates independent jobs (no conflict)
- **New key, same file**: Creates a new job (useful for re-analysis after code changes)

<Info>
Completed jobs are retained for 24 hours. After that, submitting the same idempotency key will create a new job.
</Info>
7 changes: 4 additions & 3 deletions authentication.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ X-Api-Key: smsk_live_...

## Idempotency Key

All API requests require an `Idempotency-Key` header. This is a unique value (such as a UUID) that you generate for each request. It serves two purposes:
All API requests require an `Idempotency-Key` header. This is a unique value (such as a UUID) that you generate for each request. It serves three purposes:

1. **Safe retries**: If a request fails due to a network issue, you can safely retry it with the same idempotency key without risking duplicate operations.
2. **Request tracing**: The key is echoed back in the `X-Request-Id` response header for debugging and support purposes.
1. **Job identity**: The key identifies your graph generation job. Re-submitting the same key returns the existing job status rather than creating a duplicate.
2. **Polling**: You poll for results by re-submitting the same request with the same idempotency key. See [Async Polling](/async-polling) for details.
3. **Request tracing**: The key is echoed back in the `X-Request-Id` response header for debugging and support purposes.

```bash
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
Expand Down
2 changes: 2 additions & 0 deletions concepts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ icon: 'layer-group'

Supermodel provides different "lenses" to view your codebase through. Each API endpoint generates a specific type of graph suited for different analysis tasks.

All graph generation is **asynchronous** — the API accepts your request, processes it in the background, and you poll for results. See [Async Polling](/async-polling) for details.

## Dependency Graph

**Endpoint:** `/v1/graphs/dependency`
Expand Down
1 change: 1 addition & 0 deletions docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"index",
"quickstart",
"authentication",
"async-polling",
"concepts"
]
}
Expand Down
82 changes: 57 additions & 25 deletions quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,53 +21,85 @@ The Supermodel API accepts code as a zipped archive. Navigate to your project fo
zip -r repo.zip . -x ".*" -x "**/.*" -x "node_modules/*"
```

## Step 2: Generate a graph
## Step 2: Submit a graph generation job

Use the `dependency` endpoint to generate a graph of file-level dependencies. Replace `<your-api-key>` with your actual key. The `Idempotency-Key` should be a unique value (like a UUID) for each request.
Use the `dependency` endpoint to submit a graph generation job. Replace `<your-api-key>` with your actual key. The `Idempotency-Key` should be a unique value (like a UUID) for each request.

```bash
IDEMPOTENCY_KEY=$(uuidgen)

curl --request POST \
--url https://api.supermodeltools.com/v1/graphs/dependency \
--header "Idempotency-Key: $(uuidgen)" \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--header 'X-Api-Key: <your-api-key>' \
--header 'Content-Type: multipart/form-data' \
--form file='@repo.zip'
```

The API returns an **HTTP 202 Accepted** response with the job status:

```json
{
"status": "pending",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"retryAfter": 10
}
```

<Tip>
The `$(uuidgen)` command generates a unique ID automatically on macOS and Linux. On Windows, you can use `[guid]::NewGuid()` in PowerShell.
</Tip>

## Step 3: Interpret the response
## Step 3: Poll for results

You will receive a JSON response containing the graph nodes (files) and relationships (imports).
Graph generation is asynchronous. Poll by re-submitting the same request with the **same Idempotency-Key** until the job completes:

```bash
# Re-submit the same request to check job status
curl --request POST \
--url https://api.supermodeltools.com/v1/graphs/dependency \
--header "Idempotency-Key: $IDEMPOTENCY_KEY" \
--header 'X-Api-Key: <your-api-key>' \
--header 'Content-Type: multipart/form-data' \
--form file='@repo.zip'
```

When the job completes, you receive an **HTTP 200** response with the graph inside the `result` field:

```json
{
"graph": {
"nodes": [
{
"id": "src/main.ts",
"labels": ["File"],
"properties": { "name": "main.ts" }
},
{
"id": "src/utils.ts",
"labels": ["File"],
"properties": { "name": "utils.ts" }
}
],
"relationships": [
{
"type": "imports",
"startNode": "src/main.ts",
"endNode": "src/utils.ts"
}
]
"status": "completed",
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"result": {
"graph": {
"nodes": [
{
"id": "src/main.ts",
"labels": ["File"],
"properties": { "name": "main.ts" }
},
{
"id": "src/utils.ts",
"labels": ["File"],
"properties": { "name": "utils.ts" }
}
],
"relationships": [
{
"type": "imports",
"startNode": "src/main.ts",
"endNode": "src/utils.ts"
}
]
}
}
}
```

<Info>
**Using the SDK?** The `@supermodeltools/sdk` package handles polling automatically. See the [Async Polling](/async-polling) guide for details.
</Info>

## Next Steps

Explore other graph types to get different insights into your code:
Expand Down