pot-api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +26 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +102 -0
- package/dist/jobs.d.ts +18 -0
- package/dist/jobs.js +31 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# pot-api
|
|
2
|
+
|
|
3
|
+
REST API server for [pot-sdk](https://npmjs.com/package/pot-sdk) — sync and async AI output verification with webhook callbacks.
|
|
4
|
+
|
|
5
|
+
## What it does
|
|
6
|
+
|
|
7
|
+
pot-api wraps pot-sdk as an HTTP server. Fire a verification request and get the result synchronously, or use async mode to get a `jobId` immediately while verification runs in the background — with optional webhook push on completion.
|
|
8
|
+
|
|
9
|
+
## Install & Run
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Run directly
|
|
13
|
+
ANTHROPIC_API_KEY=... XAI_API_KEY=... npx pot-api
|
|
14
|
+
|
|
15
|
+
# Or install globally
|
|
16
|
+
npm install -g pot-api
|
|
17
|
+
pot-api
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Default port: `3141`. Override with `PORT=8080`.
|
|
21
|
+
|
|
22
|
+
## Endpoints
|
|
23
|
+
|
|
24
|
+
### `POST /verify` — Sync
|
|
25
|
+
Blocks until verification is complete (~5–30s depending on tier).
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -X POST http://localhost:3141/verify \
|
|
29
|
+
-H "Content-Type: application/json" \
|
|
30
|
+
-d '{
|
|
31
|
+
"output": "The Eiffel Tower is 330m tall.",
|
|
32
|
+
"question": "How tall is the Eiffel Tower?",
|
|
33
|
+
"tier": "basic"
|
|
34
|
+
}'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Returns: `VerificationResult` (confidence, flags, mdi, sas, dissent, synthesis)
|
|
38
|
+
|
|
39
|
+
### `POST /verify/async` — Async + Webhook
|
|
40
|
+
Returns `{jobId}` immediately. Verification runs in background.
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
curl -X POST http://localhost:3141/verify/async \
|
|
44
|
+
-H "Content-Type: application/json" \
|
|
45
|
+
-d '{
|
|
46
|
+
"output": "The Eiffel Tower is 330m tall.",
|
|
47
|
+
"question": "How tall is the Eiffel Tower?",
|
|
48
|
+
"tier": "basic",
|
|
49
|
+
"callbackUrl": "https://your-agent.example.com/webhook"
|
|
50
|
+
}'
|
|
51
|
+
|
|
52
|
+
# → {"jobId":"abc-123","status":"pending","pollUrl":"/jobs/abc-123"}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
When done, pot-api POSTs to your `callbackUrl`:
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"jobId": "abc-123",
|
|
59
|
+
"status": "done",
|
|
60
|
+
"result": { "verified": true, "confidence": 0.91, ... }
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `GET /jobs/:id` — Poll Status
|
|
65
|
+
```bash
|
|
66
|
+
curl http://localhost:3141/jobs/abc-123
|
|
67
|
+
# → {"id":"abc-123","status":"running","input":{...}}
|
|
68
|
+
# → {"id":"abc-123","status":"done","result":{...}}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Status values: `pending` → `running` → `done` | `error`
|
|
72
|
+
|
|
73
|
+
## Environment Variables
|
|
74
|
+
|
|
75
|
+
| Variable | Description |
|
|
76
|
+
|----------|-------------|
|
|
77
|
+
| `ANTHROPIC_API_KEY` | ✅ Required |
|
|
78
|
+
| `XAI_API_KEY` | Optional extra generator |
|
|
79
|
+
| `DEEPSEEK_API_KEY` | Optional extra generator |
|
|
80
|
+
| `MOONSHOT_API_KEY` | Optional extra generator |
|
|
81
|
+
| `PORT` | Server port (default: 3141) |
|
|
82
|
+
|
|
83
|
+
## Per-request API keys (BYOK)
|
|
84
|
+
|
|
85
|
+
Override keys per request:
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"output": "...",
|
|
89
|
+
"question": "...",
|
|
90
|
+
"apiKeys": {
|
|
91
|
+
"anthropic": "sk-ant-...",
|
|
92
|
+
"xai": "xai-..."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Links
|
|
98
|
+
|
|
99
|
+
- pot-sdk: https://npmjs.com/package/pot-sdk
|
|
100
|
+
- pot-mcp: https://npmjs.com/package/pot-mcp
|
|
101
|
+
- Protocol spec: https://thoughtproof.ai
|
|
102
|
+
- GitHub: https://github.com/ThoughtProof/pot-api
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ApiKeys {
|
|
2
|
+
anthropic?: string;
|
|
3
|
+
xai?: string;
|
|
4
|
+
deepseek?: string;
|
|
5
|
+
moonshot?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveKeys(override?: ApiKeys): ApiKeys;
|
|
8
|
+
export declare function buildApiKeyRecord(keys: ApiKeys): Record<string, string>;
|
|
9
|
+
export declare function validateConfig(keys: ApiKeys): string | null;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function resolveKeys(override) {
|
|
2
|
+
return {
|
|
3
|
+
anthropic: override?.anthropic ?? process.env.ANTHROPIC_API_KEY,
|
|
4
|
+
xai: override?.xai ?? process.env.XAI_API_KEY,
|
|
5
|
+
deepseek: override?.deepseek ?? process.env.DEEPSEEK_API_KEY,
|
|
6
|
+
moonshot: override?.moonshot ?? process.env.MOONSHOT_API_KEY,
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function buildApiKeyRecord(keys) {
|
|
10
|
+
const record = {};
|
|
11
|
+
if (keys.anthropic)
|
|
12
|
+
record['anthropic'] = keys.anthropic;
|
|
13
|
+
if (keys.xai)
|
|
14
|
+
record['xai'] = keys.xai;
|
|
15
|
+
if (keys.deepseek)
|
|
16
|
+
record['deepseek'] = keys.deepseek;
|
|
17
|
+
if (keys.moonshot)
|
|
18
|
+
record['moonshot'] = keys.moonshot;
|
|
19
|
+
return record;
|
|
20
|
+
}
|
|
21
|
+
export function validateConfig(keys) {
|
|
22
|
+
if (!keys.anthropic) {
|
|
23
|
+
return 'ANTHROPIC_API_KEY is required.';
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import { verify } from 'pot-sdk';
|
|
4
|
+
import { resolveKeys, buildApiKeyRecord, validateConfig } from './config.js';
|
|
5
|
+
import { createJob, getJob, updateJob } from './jobs.js';
|
|
6
|
+
const app = express();
|
|
7
|
+
app.use(express.json({ limit: '1mb' }));
|
|
8
|
+
const PORT = parseInt(process.env.PORT ?? '3141', 10);
|
|
9
|
+
// ─── Health ───────────────────────────────────────────────
|
|
10
|
+
app.get('/', (_req, res) => {
|
|
11
|
+
res.json({ name: 'pot-api', version: '0.1.0', status: 'ok' });
|
|
12
|
+
});
|
|
13
|
+
// ─── Sync verify ──────────────────────────────────────────
|
|
14
|
+
app.post('/verify', async (req, res) => {
|
|
15
|
+
const { output, question, tier = 'basic', apiKeys } = req.body;
|
|
16
|
+
if (!output || !question) {
|
|
17
|
+
res.status(400).json({ error: 'output and question are required' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const keys = resolveKeys(apiKeys);
|
|
21
|
+
const configError = validateConfig(keys);
|
|
22
|
+
if (configError) {
|
|
23
|
+
res.status(400).json({ error: configError });
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const result = await verify(output, {
|
|
28
|
+
tier,
|
|
29
|
+
apiKeys: buildApiKeyRecord(keys),
|
|
30
|
+
question,
|
|
31
|
+
});
|
|
32
|
+
res.json(result);
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
// ─── Async verify ─────────────────────────────────────────
|
|
39
|
+
app.post('/verify/async', (req, res) => {
|
|
40
|
+
const { output, question, tier = 'basic', callbackUrl, apiKeys } = req.body;
|
|
41
|
+
if (!output || !question) {
|
|
42
|
+
res.status(400).json({ error: 'output and question are required' });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const keys = resolveKeys(apiKeys);
|
|
46
|
+
const configError = validateConfig(keys);
|
|
47
|
+
if (configError) {
|
|
48
|
+
res.status(400).json({ error: configError });
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const job = createJob({ output, question, tier, callbackUrl });
|
|
52
|
+
res.status(202).json({ jobId: job.id, status: 'pending', pollUrl: `/jobs/${job.id}` });
|
|
53
|
+
// Run in background
|
|
54
|
+
runJob(job.id, output, question, tier, buildApiKeyRecord(keys), callbackUrl);
|
|
55
|
+
});
|
|
56
|
+
// ─── Poll job status ───────────────────────────────────────
|
|
57
|
+
app.get('/jobs/:id', (req, res) => {
|
|
58
|
+
const job = getJob(req.params.id);
|
|
59
|
+
if (!job) {
|
|
60
|
+
res.status(404).json({ error: 'Job not found' });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
res.json(job);
|
|
64
|
+
});
|
|
65
|
+
// ─── Background runner + webhook push ────────────────────
|
|
66
|
+
async function runJob(jobId, output, question, tier, apiKeys, callbackUrl) {
|
|
67
|
+
updateJob(jobId, { status: 'running' });
|
|
68
|
+
try {
|
|
69
|
+
const result = await verify(output, { tier, apiKeys, question });
|
|
70
|
+
updateJob(jobId, { status: 'done', result });
|
|
71
|
+
if (callbackUrl) {
|
|
72
|
+
await pushWebhook(callbackUrl, { jobId, status: 'done', result });
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
77
|
+
updateJob(jobId, { status: 'error', error });
|
|
78
|
+
if (callbackUrl) {
|
|
79
|
+
await pushWebhook(callbackUrl, { jobId, status: 'error', error });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function pushWebhook(url, payload) {
|
|
84
|
+
try {
|
|
85
|
+
await fetch(url, {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
headers: { 'Content-Type': 'application/json' },
|
|
88
|
+
body: JSON.stringify(payload),
|
|
89
|
+
signal: AbortSignal.timeout(10_000),
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
console.warn(`[pot-api] Webhook delivery failed for ${url}:`, err);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ─── Start ────────────────────────────────────────────────
|
|
97
|
+
app.listen(PORT, () => {
|
|
98
|
+
console.log(`🔍 pot-api running on http://localhost:${PORT}`);
|
|
99
|
+
console.log(` POST /verify — sync verification`);
|
|
100
|
+
console.log(` POST /verify/async — async + webhook`);
|
|
101
|
+
console.log(` GET /jobs/:id — poll status`);
|
|
102
|
+
});
|
package/dist/jobs.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type JobStatus = 'pending' | 'running' | 'done' | 'error';
|
|
2
|
+
export interface Job {
|
|
3
|
+
id: string;
|
|
4
|
+
status: JobStatus;
|
|
5
|
+
createdAt: string;
|
|
6
|
+
updatedAt: string;
|
|
7
|
+
input: {
|
|
8
|
+
output: string;
|
|
9
|
+
question: string;
|
|
10
|
+
tier: 'basic' | 'pro';
|
|
11
|
+
callbackUrl?: string;
|
|
12
|
+
};
|
|
13
|
+
result?: unknown;
|
|
14
|
+
error?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function createJob(input: Job['input']): Job;
|
|
17
|
+
export declare function getJob(id: string): Job | undefined;
|
|
18
|
+
export declare function updateJob(id: string, patch: Partial<Job>): void;
|
package/dist/jobs.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
const store = new Map();
|
|
3
|
+
// Auto-cleanup jobs older than 1h
|
|
4
|
+
setInterval(() => {
|
|
5
|
+
const cutoff = Date.now() - 60 * 60 * 1000;
|
|
6
|
+
for (const [id, job] of store.entries()) {
|
|
7
|
+
if (new Date(job.createdAt).getTime() < cutoff) {
|
|
8
|
+
store.delete(id);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}, 10 * 60 * 1000);
|
|
12
|
+
export function createJob(input) {
|
|
13
|
+
const job = {
|
|
14
|
+
id: randomUUID(),
|
|
15
|
+
status: 'pending',
|
|
16
|
+
createdAt: new Date().toISOString(),
|
|
17
|
+
updatedAt: new Date().toISOString(),
|
|
18
|
+
input,
|
|
19
|
+
};
|
|
20
|
+
store.set(job.id, job);
|
|
21
|
+
return job;
|
|
22
|
+
}
|
|
23
|
+
export function getJob(id) {
|
|
24
|
+
return store.get(id);
|
|
25
|
+
}
|
|
26
|
+
export function updateJob(id, patch) {
|
|
27
|
+
const job = store.get(id);
|
|
28
|
+
if (job) {
|
|
29
|
+
Object.assign(job, patch, { updatedAt: new Date().toISOString() });
|
|
30
|
+
}
|
|
31
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pot-api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "REST API server for pot-sdk — sync and async AI output verification with webhook callbacks",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pot-api": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/index.js",
|
|
11
|
+
"files": ["dist", "README.md"],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepare": "npm run build && chmod +x dist/index.js",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"start": "node dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"express": "^4.18.0",
|
|
20
|
+
"pot-sdk": "^0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/express": "^4.17.0",
|
|
24
|
+
"@types/node": "^25.3.0",
|
|
25
|
+
"typescript": "^5.3.0"
|
|
26
|
+
}
|
|
27
|
+
}
|