openpond-sdk 0.12.23 → 0.12.25
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/PRODUCT_INTEGRATION.md +134 -0
- package/README.md +12 -1
- package/TASK_INGESTION.md +88 -86
- package/TRAINING_PROTOCOL.md +6 -2
- package/TRAINING_WORKFLOWS.md +16 -0
- package/dist/advanced-refiner-evaluations.js +3 -3
- package/dist/ai-sdk.js +224 -0
- package/dist/ai-sdk.js.map +1 -0
- package/dist/{chunk-NZRGRUYG.js → chunk-2OVNKPBZ.js} +17 -6
- package/dist/chunk-2OVNKPBZ.js.map +1 -0
- package/dist/{chunk-QIGMNB6M.js → chunk-3G3HUA32.js} +3 -3
- package/dist/{chunk-7G47YXQT.js → chunk-3MWFN7BA.js} +2 -2
- package/dist/{chunk-3BYFS4RO.js → chunk-47THZRUM.js} +5 -5
- package/dist/chunk-BRP3EBCR.js +245 -0
- package/dist/chunk-BRP3EBCR.js.map +1 -0
- package/dist/{chunk-BNBPKCIR.js → chunk-DDCR4KB5.js} +4 -4
- package/dist/{chunk-ZQ6QTYRE.js → chunk-GV6DEDBU.js} +10 -10
- package/dist/{chunk-ZQ6QTYRE.js.map → chunk-GV6DEDBU.js.map} +1 -1
- package/dist/{chunk-QCRNQMN4.js → chunk-IXFUYPGV.js} +2 -2
- package/dist/{chunk-Q4RGCEOI.js → chunk-JOMZJXGC.js} +3 -3
- package/dist/{chunk-OBI2MQYU.js → chunk-QRYEXPG7.js} +7 -7
- package/dist/{chunk-6EZAGTZG.js → chunk-RN5MK7NH.js} +4 -4
- package/dist/{chunk-GCUWJUUM.js → chunk-RTLJV4OS.js} +2 -2
- package/dist/{chunk-GBDBKGXW.js → chunk-T67LNKAK.js} +3 -3
- package/dist/{chunk-SP7DQEQM.js → chunk-XBFOFTK3.js} +5 -5
- package/dist/{chunk-AG6UJ3DI.js → chunk-YS7DF76T.js} +2 -2
- package/dist/{chunk-LEVXD73U.js → chunk-Z5APNTZE.js} +197 -190
- package/dist/chunk-Z5APNTZE.js.map +1 -0
- package/dist/connected-evidence.js +8 -8
- package/dist/dataset-workspaces.js +146 -85
- package/dist/dataset-workspaces.js.map +1 -1
- package/dist/experiment-evaluation-schedules.js +11 -11
- package/dist/experiments.js +15 -15
- package/dist/index.js +71 -44
- package/dist/index.js.map +1 -1
- package/dist/model-starter-catalog.js +2 -2
- package/dist/model-starters.js +2 -2
- package/dist/taskset-drafts.js +7 -7
- package/dist/taskset-packages.js +13 -13
- package/dist/training-bundle.js +12 -12
- package/dist/training.js +1936 -1809
- package/dist/training.js.map +1 -1
- package/dist/types/packages/sdk/src/ai-sdk.d.ts +6 -0
- package/dist/types/packages/sdk/src/ai-sdk.d.ts.map +1 -0
- package/dist/types/packages/sdk/src/candidate-evaluation-client.d.ts +21 -21
- package/dist/types/packages/sdk/src/dataset-task-ingestion.d.ts +8 -1
- package/dist/types/packages/sdk/src/dataset-task-ingestion.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/dataset-workspace-client.d.ts +140 -140
- package/dist/types/packages/sdk/src/dataset-workspace-client.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/dataset-workspace-error.d.ts +28 -0
- package/dist/types/packages/sdk/src/dataset-workspace-error.d.ts.map +1 -0
- package/dist/types/packages/sdk/src/dataset-workspaces.d.ts +1 -0
- package/dist/types/packages/sdk/src/dataset-workspaces.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/experiment-client.d.ts +636 -636
- package/dist/types/packages/sdk/src/experiment-draft-storage.d.ts +124 -34
- package/dist/types/packages/sdk/src/experiment-draft-storage.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/experiment-evaluation-schedules.d.ts +28 -28
- package/dist/types/packages/sdk/src/experiment-improvement-client.d.ts +1622 -1622
- package/dist/types/packages/sdk/src/experiment-run-details.d.ts +7 -7
- package/dist/types/packages/sdk/src/index.d.ts +13 -4
- package/dist/types/packages/sdk/src/index.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/managed-decision-executions.d.ts +347 -347
- package/dist/types/packages/sdk/src/managed-neuron-training-executions.d.ts +242 -21
- package/dist/types/packages/sdk/src/managed-neuron-training-executions.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/managed-training-preparation.d.ts +25 -25
- package/dist/types/packages/sdk/src/model-taskset-runs.d.ts +28 -28
- package/dist/types/packages/sdk/src/neuron-training-artifact.d.ts +42 -3
- package/dist/types/packages/sdk/src/neuron-training-artifact.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/neuron-training-experiment-draft.d.ts +16 -1
- package/dist/types/packages/sdk/src/neuron-training-experiment-draft.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/neuron-training-model-serving.d.ts +168 -16
- package/dist/types/packages/sdk/src/neuron-training-model-serving.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/neuron-training-result.d.ts +42 -3
- package/dist/types/packages/sdk/src/neuron-training-result.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/neuron-training.d.ts +23 -2
- package/dist/types/packages/sdk/src/neuron-training.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/post-training.d.ts +7 -7
- package/dist/types/packages/sdk/src/product-model-response.d.ts +4 -0
- package/dist/types/packages/sdk/src/product-model-response.d.ts.map +1 -0
- package/dist/types/packages/sdk/src/product-models.d.ts +76 -0
- package/dist/types/packages/sdk/src/product-models.d.ts.map +1 -0
- package/dist/types/packages/sdk/src/steering-model-serving.d.ts +42 -4
- package/dist/types/packages/sdk/src/steering-model-serving.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/steering-profile-publication.d.ts +84 -8
- package/dist/types/packages/sdk/src/steering-profile-publication.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/taskset-draft-source.d.ts +20 -20
- package/dist/types/packages/sdk/src/taskset-package-projection.d.ts +20 -20
- package/dist/types/packages/sdk/src/training-evidence.d.ts +54 -0
- package/dist/types/packages/sdk/src/training-evidence.d.ts.map +1 -0
- package/dist/types/packages/sdk/src/training-experiment-setup.d.ts +42 -2
- package/dist/types/packages/sdk/src/training-experiment-setup.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/training-harness-sources.d.ts +2 -2
- package/dist/types/packages/sdk/src/training-learning-batch.d.ts +40 -40
- package/dist/types/packages/sdk/src/training-preparations.d.ts +1770 -485
- package/dist/types/packages/sdk/src/training-preparations.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/training-qualification-preparation.d.ts +25 -25
- package/dist/types/packages/sdk/src/training-usage-funding.d.ts +23 -0
- package/dist/types/packages/sdk/src/training-usage-funding.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/training.d.ts +5189 -4445
- package/dist/types/packages/sdk/src/training.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/types.d.ts +14 -1
- package/dist/types/packages/sdk/src/types.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/weight-experiment-authoring.d.ts +5 -2
- package/dist/types/packages/sdk/src/weight-experiment-authoring.d.ts.map +1 -1
- package/dist/types/packages/sdk/src/weight-model-versions.d.ts +168 -16
- package/dist/types/packages/sdk/src/weight-model-versions.d.ts.map +1 -1
- package/fixtures/personalized-inference-request.schema.json +316 -0
- package/fixtures/python-foundation/schemas.json +8750 -8351
- package/package.json +18 -3
- package/dist/chunk-LEVXD73U.js.map +0 -1
- package/dist/chunk-NZRGRUYG.js.map +0 -1
- /package/dist/{chunk-QIGMNB6M.js.map → chunk-3G3HUA32.js.map} +0 -0
- /package/dist/{chunk-7G47YXQT.js.map → chunk-3MWFN7BA.js.map} +0 -0
- /package/dist/{chunk-3BYFS4RO.js.map → chunk-47THZRUM.js.map} +0 -0
- /package/dist/{chunk-BNBPKCIR.js.map → chunk-DDCR4KB5.js.map} +0 -0
- /package/dist/{chunk-QCRNQMN4.js.map → chunk-IXFUYPGV.js.map} +0 -0
- /package/dist/{chunk-Q4RGCEOI.js.map → chunk-JOMZJXGC.js.map} +0 -0
- /package/dist/{chunk-OBI2MQYU.js.map → chunk-QRYEXPG7.js.map} +0 -0
- /package/dist/{chunk-6EZAGTZG.js.map → chunk-RN5MK7NH.js.map} +0 -0
- /package/dist/{chunk-GCUWJUUM.js.map → chunk-RTLJV4OS.js.map} +0 -0
- /package/dist/{chunk-GBDBKGXW.js.map → chunk-T67LNKAK.js.map} +0 -0
- /package/dist/{chunk-SP7DQEQM.js.map → chunk-XBFOFTK3.js.map} +0 -0
- /package/dist/{chunk-AG6UJ3DI.js.map → chunk-YS7DF76T.js.map} +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Use OpenPond in your product
|
|
2
|
+
|
|
3
|
+
The APIs below target the **0.12.25 source candidate**, which is not published
|
|
4
|
+
yet. Registry version 0.12.24 does not include configured Dataset/named-model
|
|
5
|
+
setup. Use the built candidate package until the coordinated release.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Configure one application-owned server client. Bind aliases to exact callable
|
|
9
|
+
Model IDs from your serving destination:
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createOpenPondClient } from "openpond-sdk";
|
|
13
|
+
|
|
14
|
+
export const openpond = createOpenPondClient({
|
|
15
|
+
apiKey: process.env.OPENPOND_API_KEY!,
|
|
16
|
+
teamId: process.env.OPENPOND_TEAM_ID!,
|
|
17
|
+
datasetId: process.env.OPENPOND_DATASET_ID!,
|
|
18
|
+
serving: {
|
|
19
|
+
baseUrl: process.env.OPENPOND_SERVING_BASE_URL!,
|
|
20
|
+
apiKey: process.env.OPENPOND_SERVING_API_KEY!,
|
|
21
|
+
},
|
|
22
|
+
models: { support: process.env.OPENPOND_MODEL_ID! },
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const answer = await openpond.models.support.generate({ prompt: "How do I rotate a token?" });
|
|
26
|
+
console.log(answer.text, answer.requestId, answer.usage);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`serving.baseUrl` is the personalized OpenAI-compatible Chat Completions base URL,
|
|
30
|
+
including its `/v1` path. It is configured explicitly: no catalogue default or
|
|
31
|
+
substitute Model is selected. Omit `serving.apiKey` only when this destination
|
|
32
|
+
accepts the main API key. Naming a Model never activates serving, approves a
|
|
33
|
+
candidate, starts training, or automatically captures inference as a task.
|
|
34
|
+
Decision scorers use their separate typed scoring APIs.
|
|
35
|
+
|
|
36
|
+
`generate` accepts either `prompt` or text `messages`, plus optional `temperature`,
|
|
37
|
+
`maxTokens` and `signal`. Results include `text`, `requestId`, requested `modelId`,
|
|
38
|
+
server-reported `resolvedModelId`, `finishReason`, nullable `usage`, and nullable
|
|
39
|
+
`systemFingerprint`, plus retained server `evidence` when supplied. Evidence
|
|
40
|
+
presence is not client-side verification. Core handles are text APIs; tools need
|
|
41
|
+
the supported integration and explicit capabilities. The current personalized API
|
|
42
|
+
rejects multimodal and structured-output modes. Aliases are typed TypeScript
|
|
43
|
+
properties; multiple clients remain independent.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
for await (const event of openpond.models.support.stream({ prompt: "Explain token rotation" })) {
|
|
47
|
+
if (event.type === "text") process.stdout.write(event.text);
|
|
48
|
+
else console.log(event.result.usage);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Breaking the iteration cancels the response. An interrupted stream raises
|
|
53
|
+
`ProductModelError` and never emits a successful `finish`. It may already have
|
|
54
|
+
incurred usage; inference is never automatically replayed. `serving.timeoutMs`
|
|
55
|
+
defaults to 60 seconds and accepts at most 300 seconds. Supply a caller abort
|
|
56
|
+
signal to cancel an individual request. Text requests are bounded to 1 MiB and
|
|
57
|
+
results to 8 MiB; individual stream frames to 1 MiB.
|
|
58
|
+
|
|
59
|
+
Await task delivery separately with `openpond.addTask(...)`. If inference succeeds
|
|
60
|
+
and optional task delivery fails, preserve both outcomes rather than discarding
|
|
61
|
+
the delivery error or pretending the answer failed. See [task ingestion](TASK_INGESTION.md).
|
|
62
|
+
|
|
63
|
+
## Python sync and async
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from ponder_sdk import AsyncClient
|
|
67
|
+
|
|
68
|
+
async with AsyncClient(
|
|
69
|
+
base_url=api_origin, api_key=api_key, team_id=team_id,
|
|
70
|
+
dataset_id=dataset_id,
|
|
71
|
+
serving_base_url=serving_base_url, serving_api_key=serving_api_key,
|
|
72
|
+
model_aliases={"support": model_id},
|
|
73
|
+
) as openpond:
|
|
74
|
+
answer = await openpond.models.support.generate(prompt="How do I rotate a token?")
|
|
75
|
+
print(answer.text, answer.request_id, answer.usage)
|
|
76
|
+
await openpond.add_task({"id": event_id, "name": "Token rotation", "input": {"question": question}})
|
|
77
|
+
async for event in openpond.models.support.stream(prompt="Explain the steps"):
|
|
78
|
+
if event.type == "text":
|
|
79
|
+
print(event.text, end="")
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
`Client` has the same configuration and synchronous calls/iteration. Named aliases
|
|
83
|
+
augment existing Model administration methods; choose aliases that don't collide
|
|
84
|
+
with those methods. Bracket access (`openpond.models["support"]`) is also available.
|
|
85
|
+
Inference shares the existing client's HTTP pool and timeout. Close partially
|
|
86
|
+
consumed iterators (or use `contextlib.closing`/`aclosing`) to release streams
|
|
87
|
+
promptly. Native async cancellation propagates. No SDK queues or flush calls exist.
|
|
88
|
+
|
|
89
|
+
## Optional Vercel AI SDK 7 integration
|
|
90
|
+
|
|
91
|
+
Install `ai@^7.0.165` and `@ai-sdk/openai-compatible@^3.0.87` alongside OpenPond.
|
|
92
|
+
Only the `openpond-sdk/ai-sdk` entry point imports the optional provider; task
|
|
93
|
+
submission has no Vercel dependency. This adapter targets LanguageModelV4 and is
|
|
94
|
+
not an upgrade of an application's existing AI SDK 4/5/6 installation.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { generateText, streamText } from "ai";
|
|
98
|
+
import { toAIModel } from "openpond-sdk/ai-sdk";
|
|
99
|
+
import { openpond } from "./openpond.server.js";
|
|
100
|
+
|
|
101
|
+
const model = toAIModel(openpond.models.support);
|
|
102
|
+
const answer = await generateText({ model, prompt: "Explain token rotation", maxRetries: 0 });
|
|
103
|
+
|
|
104
|
+
// Inside an authenticated product backend route:
|
|
105
|
+
export async function POST(request: Request) {
|
|
106
|
+
// Authenticate the product user and validate/limit this payload first.
|
|
107
|
+
const { prompt } = await request.json();
|
|
108
|
+
const result = streamText({ model, prompt, abortSignal: request.signal, maxRetries: 0 });
|
|
109
|
+
return result.toUIMessageStreamResponse();
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Use the AI SDK 7 React `useChat`/transport recipe against your own authenticated
|
|
114
|
+
backend. Never place the OpenPond client or secrets in browser code. AI SDK's UI
|
|
115
|
+
stream response is different from raw OpenAI SSE; use
|
|
116
|
+
`toUIMessageStreamResponse()` rather than forwarding raw SSE into `useChat`.
|
|
117
|
+
|
|
118
|
+
The official compatible provider handles content/usage conversion, tool deltas and
|
|
119
|
+
stream cancellation. OpenPond gates features against the selected binding:
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
models: {
|
|
123
|
+
support: {
|
|
124
|
+
id: process.env.OPENPOND_MODEL_ID!,
|
|
125
|
+
capabilities: { streaming: true, tools: true },
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Only enable tools after checking that exact deployment.
|
|
131
|
+
Defaults are streaming enabled and tools disabled. Structured-output and multimodal
|
|
132
|
+
inputs are rejected. Failed provider calls are surfaced as non-retryable SDK
|
|
133
|
+
errors to prevent implicit replay of billed requests. Explicit `maxRetries: 0`
|
|
134
|
+
also makes that intent visible in application code.
|
package/README.md
CHANGED
|
@@ -392,4 +392,15 @@ not implicitly share a spending cap.
|
|
|
392
392
|
|
|
393
393
|
## Add production tasks
|
|
394
394
|
|
|
395
|
-
|
|
395
|
+
The configured API targets unpublished source candidate **0.12.25**; registry 0.12.24 does not provide it. Configure `datasetId` once and await `client.addTask({ id: sourceEventId, name, input })`. Use `client.dataset(otherDatasetId).addTask(task)` for another destination. See [task ingestion](TASK_INGESTION.md) for TypeScript/Python examples, receipt semantics and duplicate-safe retries. Task uploads do not enable training.
|
|
396
|
+
|
|
397
|
+
Dataset workspace HTTP failures throw `DatasetWorkspaceRequestError` from
|
|
398
|
+
`openpond-sdk/dataset-workspaces`, with `status`, an allowlisted `code`,
|
|
399
|
+
`retryable`, and `outcomeUnknown`. Messages contain fixed recovery guidance;
|
|
400
|
+
raw server diagnostics are discarded. `retryable` does not trigger an automatic
|
|
401
|
+
retry. When `outcomeUnknown` is true, retain the original operation ID and request
|
|
402
|
+
bytes, recover its receipt with `operationResult`, or retry the identical request.
|
|
403
|
+
|
|
404
|
+
## Add OpenPond to a product
|
|
405
|
+
|
|
406
|
+
Configure named personalized Model handles with `models` and an explicit `serving` destination, then call `client.models.support.generate({ prompt })` or iterate `stream({ prompt })`. See [product integration](PRODUCT_INTEGRATION.md) for TypeScript, Python, React/backend and optional Vercel AI SDK 7 examples. Inference never activates serving or automatically captures tasks.
|
package/TASK_INGESTION.md
CHANGED
|
@@ -1,135 +1,137 @@
|
|
|
1
1
|
# Add tasks to a Dataset
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
a release, create a Model or enable a collection schedule.
|
|
3
|
+
The APIs below target the **0.12.25 source candidate**, which is not published
|
|
4
|
+
yet. Registry version 0.12.24 does not include configured Dataset/named-model
|
|
5
|
+
setup. Use the built candidate package until the coordinated release.
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
|
|
8
|
+
Configure a client once in your product's server module. Submit one structured
|
|
9
|
+
source event with `await addTask(task)`:
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
import { createOpenPondClient } from "openpond-sdk";
|
|
13
13
|
|
|
14
|
-
const
|
|
14
|
+
export const openpond = createOpenPondClient({
|
|
15
15
|
apiKey: process.env.OPENPOND_API_KEY!,
|
|
16
16
|
teamId: process.env.OPENPOND_TEAM_ID!,
|
|
17
|
+
datasetId: process.env.OPENPOND_DATASET_ID!,
|
|
17
18
|
});
|
|
18
19
|
|
|
19
|
-
const receipt = await
|
|
20
|
+
const receipt = await openpond.addTask({
|
|
21
|
+
id: sourceEventId,
|
|
20
22
|
name: "Handle an expired API token",
|
|
21
23
|
input: { request: "Fetch the report", observedError: "Token expired" },
|
|
22
|
-
source: { kind: "production_agent", eventId:
|
|
23
|
-
}
|
|
24
|
-
|
|
24
|
+
source: { kind: "production_agent", eventId: sourceEventId },
|
|
25
|
+
});
|
|
25
26
|
console.log(receipt.taskId, receipt.receivedAt);
|
|
26
27
|
```
|
|
27
28
|
|
|
28
|
-
Use
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
## Bind the destination once
|
|
29
|
+
Use `baseUrl` for a different OpenPond API origin. Each configured client keeps
|
|
30
|
+
its own credentials and destination; no mutable global account is used. For an
|
|
31
|
+
explicit alternate Dataset:
|
|
32
32
|
|
|
33
33
|
```ts
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const addTask = createTaskAdder({
|
|
37
|
-
apiKey: process.env.OPENPOND_API_KEY!,
|
|
38
|
-
teamId: process.env.OPENPOND_TEAM_ID!,
|
|
39
|
-
datasetId: process.env.OPENPOND_DATASET_ID!,
|
|
40
|
-
baseUrl: "https://api.openpond.ai",
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
await addTask({
|
|
34
|
+
await openpond.dataset(otherDatasetId).addTask({
|
|
35
|
+
id: sourceEventId,
|
|
44
36
|
name: "Explain a failed agent run",
|
|
45
37
|
input: { request: "Summarize the report", outcome: "incomplete" },
|
|
46
|
-
}
|
|
38
|
+
});
|
|
47
39
|
```
|
|
48
40
|
|
|
49
|
-
|
|
50
|
-
it never uses a global account, last-selected Dataset or implicit Fix-it Model.
|
|
41
|
+
`addTask(datasetId, task)` and the `createTaskAdder` helper remain available.
|
|
51
42
|
|
|
52
|
-
##
|
|
43
|
+
## Delivery, identity and retries
|
|
53
44
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
A successful receipt confirms durable storage of the task and its processing
|
|
46
|
+
obligation. It does not wait for indexing, grading or training. Dataset projection
|
|
47
|
+
may lag acknowledgement. Await suspends this async function while the JavaScript
|
|
48
|
+
event loop continues handling other work. There is no SDK background queue,
|
|
49
|
+
flush command or permanent streaming connection.
|
|
59
50
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
51
|
+
`id` identifies the source event within the workspace and Dataset. It is separate
|
|
52
|
+
from the server's canonical `receipt.taskId`. Repeating the same source ID and
|
|
53
|
+
content returns the original receipt; changed content conflicts instead of
|
|
54
|
+
silently overwriting. New credentials still need current authorization for that
|
|
55
|
+
exact Dataset. A successful upload grants no training permission.
|
|
64
56
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
Retain an explicit stable idempotency key for each source event. Repeating the
|
|
68
|
-
same key and task in the same authorized destination returns the original
|
|
69
|
-
receipt without inserting a duplicate. Reusing a key with different content is
|
|
70
|
-
a conflict. Separate keys add separate tasks, including simultaneous writers.
|
|
71
|
-
|
|
72
|
-
The SDK makes one request; it does not silently replay arbitrary mutations.
|
|
73
|
-
When a response is lost, `DatasetTaskIngestionError.operationId` retains the
|
|
74
|
-
operation identity and `outcomeUnknown` indicates that persistence could not be
|
|
75
|
-
confirmed. Retry the original task with that same key. Do not generate a new
|
|
76
|
-
key merely because the response was interrupted. A supplied abort signal is
|
|
77
|
-
forwarded to the request; an interrupted response can still have been saved.
|
|
57
|
+
The SDK sends once. It never automatically replays a mutation. Preserve your
|
|
58
|
+
source ID and original content across uncertain outcomes:
|
|
78
59
|
|
|
79
60
|
```ts
|
|
80
61
|
import { DatasetTaskIngestionError } from "openpond-sdk";
|
|
81
62
|
|
|
63
|
+
const task = { id: sourceEventId, name: "Expired token", input: { error: "expired" } };
|
|
82
64
|
try {
|
|
83
|
-
await addTask(task, {
|
|
65
|
+
await openpond.addTask(task, { signal: request.signal, timeoutMs: 10_000 });
|
|
84
66
|
} catch (error) {
|
|
85
67
|
if (error instanceof DatasetTaskIngestionError && error.outcomeUnknown) {
|
|
86
|
-
// Retain task
|
|
68
|
+
// Retain task and error.sourceId privately for a later exact retry.
|
|
87
69
|
}
|
|
88
70
|
throw error;
|
|
89
71
|
}
|
|
90
72
|
```
|
|
91
73
|
|
|
92
|
-
|
|
93
|
-
`
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
The server owns authorization, atomic append and revision history. Appending
|
|
107
|
-
does not rewrite fixed Experiment Dataset versions or previously published
|
|
108
|
-
releases. Full authoring still uses the revisioned Dataset workspace API.
|
|
74
|
+
When `id` is omitted the SDK generates one before dispatch. On uncertain delivery,
|
|
75
|
+
`DatasetTaskIngestionError.sourceId` (also `operationId`) retains it. Supply that
|
|
76
|
+
ID in a later retry. Prefer an explicit ID when cancellation or process exit could
|
|
77
|
+
prevent handling the error. An event never transmitted successfully cannot be
|
|
78
|
+
promised durable. `idempotencyKey` is accepted for existing callers; supplying a
|
|
79
|
+
conflicting `task.id` is rejected before dispatch.
|
|
80
|
+
|
|
81
|
+
Every task has `name` and structured `input`. Optional fields: `expectedOutput`,
|
|
82
|
+
`split`, `tags`, and `source` (`kind`, `eventId`, `label`, `occurredAt`). Default
|
|
83
|
+
split: `validation`; `train` alone grants no learning authority. Requests are
|
|
84
|
+
bounded to 128 KiB. The receipt retains workspace, Dataset, canonical task and
|
|
85
|
+
source identities, first-save `receivedAt`, revision, `state: "saved"` and
|
|
86
|
+
`trainingUseAuthorized: false`. Source occurrence time differs from upload time.
|
|
87
|
+
Existing immutable releases and Experiment inputs retain their own identities.
|
|
109
88
|
|
|
110
89
|
## Python
|
|
111
90
|
|
|
112
|
-
|
|
113
|
-
|
|
91
|
+
`ponder-sdk` uses the same configured destination and source ID with its native
|
|
92
|
+
owned connection pool:
|
|
114
93
|
|
|
115
94
|
```python
|
|
116
95
|
from ponder_sdk import Client
|
|
117
96
|
|
|
118
|
-
with Client(base_url=api_origin, api_key=api_key, team_id=team_id
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
97
|
+
with Client(base_url=api_origin, api_key=api_key, team_id=team_id,
|
|
98
|
+
dataset_id=dataset_id) as openpond:
|
|
99
|
+
receipt = openpond.add_task({
|
|
100
|
+
"id": source_event_id,
|
|
101
|
+
"name": "Expired token",
|
|
102
|
+
"input": {"error": "Token expired"},
|
|
103
|
+
})
|
|
104
|
+
# Alternate destination; uses the same pool:
|
|
105
|
+
# openpond.dataset(other_dataset_id).add_task(task)
|
|
124
106
|
```
|
|
125
107
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
108
|
+
Sync calls block their calling thread. Async applications use
|
|
109
|
+
`async with AsyncClient(...)` and `await openpond.add_task(task)`. Both validate the
|
|
110
|
+
maintained wire schemas and never replay writes automatically. A `PonderError`
|
|
111
|
+
retains `operation_id` and `outcome_unknown`; cancellation propagates, so retain
|
|
112
|
+
your own source ID before awaiting. Closing the HTTP pool is resource cleanup,
|
|
113
|
+
not delivery flushing.
|
|
114
|
+
|
|
115
|
+
## HTTP and React
|
|
116
|
+
|
|
117
|
+
The HTTP operation remains `POST /v1/dataset-workspaces/{datasetId}/tasks` with
|
|
118
|
+
Bearer auth and `X-OpenPond-Team-Id`. The public `task.id` maps to wire
|
|
119
|
+
`operationId`; it is excluded from `task` content:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"schemaVersion": "openpond.datasetTaskAppend.v1",
|
|
124
|
+
"operationId": "stable-source-event",
|
|
125
|
+
"task": { "name": "Expired token", "input": { "error": "expired" } }
|
|
126
|
+
}
|
|
127
|
+
```
|
|
129
128
|
|
|
130
|
-
|
|
129
|
+
React calls your authenticated product backend. The backend selects an authorized
|
|
130
|
+
Dataset and awaits the configured client. Keep OpenPond secrets and destination
|
|
131
|
+
authority server-side; do not accept an arbitrary browser-supplied Dataset ID.
|
|
132
|
+
For named model calls and the optional Vercel integration see
|
|
133
|
+
[product integration](PRODUCT_INTEGRATION.md).
|
|
131
134
|
|
|
132
|
-
OpenTelemetry is
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
OTLP receiver, automatically capture arbitrary logs, or authorize training.
|
|
135
|
+
OpenTelemetry is a separate optional source adapter, not this request format.
|
|
136
|
+
Project explicitly selected spans into structured tasks. This endpoint is not an
|
|
137
|
+
OTLP receiver and does not capture arbitrary logs or authorize training.
|
package/TRAINING_PROTOCOL.md
CHANGED
|
@@ -50,8 +50,8 @@ GET /v1/training/jobs?modelProjectId=&cursor=&limit=
|
|
|
50
50
|
GET /v1/training/jobs/{jobId}
|
|
51
51
|
POST /v1/training/jobs/{jobId}/cancel
|
|
52
52
|
POST /v1/training/jobs/{jobId}/stop-after-group
|
|
53
|
-
GET /v1/training/jobs/{jobId}/events
|
|
54
|
-
GET /v1/training/jobs/{jobId}/logs
|
|
53
|
+
GET /v1/training/jobs/{jobId}/events?after=0&limit=50
|
|
54
|
+
GET /v1/training/jobs/{jobId}/logs?after=0&limit=50
|
|
55
55
|
GET /v1/training/jobs/{jobId}/outputs
|
|
56
56
|
|
|
57
57
|
PUT /v1/model-projects/{portableProjectId}
|
|
@@ -328,3 +328,7 @@ It performs no authentication, network upload, tool execution, training, or mode
|
|
|
328
328
|
activation. Callers must verify authentic durable hosted receipts before
|
|
329
329
|
acknowledging delivery. Import the portable entry in browser code; the Node queue
|
|
330
330
|
is a separate runtime capability.
|
|
331
|
+
|
|
332
|
+
### Retained evidence pagination
|
|
333
|
+
|
|
334
|
+
Events and logs return at most 100 records per database-selected page (default 50), plus `nextOffset` or `null`. Use `training.eventsPage(jobId, { after, limit })` and `training.logsPage(...)` to obtain `{ items, nextOffset }`. The SDK validates the returned job, sequence window, count and continuation before exposing records. Offsets address the current ordered snapshot; lifecycle snapshots can change while a run is live. They are not an immutable append-only cursor. Existing `events()`/`logs()` convenience reads follow pages with an aggregate response-byte budget and a 100,000-record ceiling. MCP requests exactly one page.
|
package/TRAINING_WORKFLOWS.md
CHANGED
|
@@ -653,3 +653,19 @@ evaluation remains an independent candidate check. A nightly schedule uses the
|
|
|
653
653
|
authored time and timezone, waits for approved unused examples, and respects the
|
|
654
654
|
saved task threshold and spend limits. It does not itself enable conversation
|
|
655
655
|
collection, automatic data approval, or automatic serving.
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
Selective neuron continuation uses an exact compatible saved neuron version.
|
|
659
|
+
`neuronTrainingFormFromModelVersion` retains that version and its cumulative
|
|
660
|
+
patch, copies the parent's selection configuration, clears the target choice,
|
|
661
|
+
and requires reviewed new target data. `background=[]` retains the original
|
|
662
|
+
selection as inherited evidence; it does not report selection on the new data.
|
|
663
|
+
The explicit mode is `frozen_parent_fresh_residual_adam_v1`: the parent stays
|
|
664
|
+
frozen, fresh zero column parameters use the original dense base as their
|
|
665
|
+
anchors, and a fresh Adam optimizer learns a residual. No optimizer or RNG
|
|
666
|
+
checkpoint is resumed. Optional preservation compares against the starting
|
|
667
|
+
base plus parent patch. The worker verifies the exact parent bytes before
|
|
668
|
+
optimizer creation and exports a cumulative patch with parent, initial and
|
|
669
|
+
final state hashes. A runtime must explicitly advertise this mode before
|
|
670
|
+
admission; source packaging alone does not establish native qualification or
|
|
671
|
+
old/new held-out retention.
|
|
@@ -12,12 +12,12 @@ import {
|
|
|
12
12
|
sealAdvancedRefinerEvaluationPin,
|
|
13
13
|
sealAdvancedRefinerReviewReceipt,
|
|
14
14
|
verifyAdvancedRefinerEvaluationPin
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
import "./chunk-3B5KSH3V.js";
|
|
17
|
-
import "./chunk-H3367ISL.js";
|
|
15
|
+
} from "./chunk-RN5MK7NH.js";
|
|
18
16
|
import "./chunk-QNOCM5JX.js";
|
|
17
|
+
import "./chunk-3B5KSH3V.js";
|
|
19
18
|
import "./chunk-626JLH4D.js";
|
|
20
19
|
import "./chunk-O6FKHAWN.js";
|
|
20
|
+
import "./chunk-H3367ISL.js";
|
|
21
21
|
import "./chunk-GS5HEE4M.js";
|
|
22
22
|
import "./chunk-ZRTNR4U7.js";
|
|
23
23
|
import "./chunk-JMFYNIYZ.js";
|
package/dist/ai-sdk.js
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ProductModelError,
|
|
3
|
+
productModelEndpoint
|
|
4
|
+
} from "./chunk-BRP3EBCR.js";
|
|
5
|
+
import "./chunk-B3ZHJ2KK.js";
|
|
6
|
+
import "./chunk-YLJ4XMA6.js";
|
|
7
|
+
|
|
8
|
+
// src/ai-sdk.ts
|
|
9
|
+
import {
|
|
10
|
+
createOpenAICompatible
|
|
11
|
+
} from "@ai-sdk/openai-compatible";
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
|
|
14
|
+
// src/product-model-response.ts
|
|
15
|
+
function boundProductModelResponse(response) {
|
|
16
|
+
if (!response.body) return response;
|
|
17
|
+
const streaming = response.headers.get("content-type")?.includes("text/event-stream") ?? false;
|
|
18
|
+
const reader = response.body.getReader();
|
|
19
|
+
const decoder = new TextDecoder();
|
|
20
|
+
let total = 0, pending = "", terminal = false;
|
|
21
|
+
let identity;
|
|
22
|
+
const fail = () => new ProductModelError(
|
|
23
|
+
streaming ? "model_stream_interrupted" : "model_response_invalid",
|
|
24
|
+
response.status,
|
|
25
|
+
true,
|
|
26
|
+
response.headers.get("x-openpond-request-id") ?? response.headers.get("x-request-id") ?? void 0
|
|
27
|
+
);
|
|
28
|
+
const body = new ReadableStream({
|
|
29
|
+
async pull(controller) {
|
|
30
|
+
try {
|
|
31
|
+
const { done, value } = await reader.read();
|
|
32
|
+
if (done) {
|
|
33
|
+
if (streaming && !terminal) throw fail();
|
|
34
|
+
controller.close();
|
|
35
|
+
reader.releaseLock();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
total += value.byteLength;
|
|
39
|
+
if (total > 8 * 1024 * 1024) throw fail();
|
|
40
|
+
if (streaming) {
|
|
41
|
+
pending = (pending + decoder.decode(value, { stream: true })).replace(/\r\n/g, "\n");
|
|
42
|
+
let boundary;
|
|
43
|
+
while ((boundary = pending.indexOf("\n\n")) >= 0) {
|
|
44
|
+
const frame = pending.slice(0, boundary);
|
|
45
|
+
pending = pending.slice(boundary + 2);
|
|
46
|
+
if (new TextEncoder().encode(frame).length > 1024 * 1024) throw fail();
|
|
47
|
+
const data = frame.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trimStart()).join("\n");
|
|
48
|
+
if (data === "[DONE]") terminal = true;
|
|
49
|
+
else if (data) {
|
|
50
|
+
const chunk = JSON.parse(data);
|
|
51
|
+
if (typeof chunk.id === "string" && typeof chunk.model === "string") {
|
|
52
|
+
if (identity && (chunk.id !== identity.id || chunk.model !== identity.model))
|
|
53
|
+
throw fail();
|
|
54
|
+
identity = { id: chunk.id, model: chunk.model };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (new TextEncoder().encode(pending).length > 1024 * 1024) throw fail();
|
|
59
|
+
}
|
|
60
|
+
controller.enqueue(value);
|
|
61
|
+
} catch {
|
|
62
|
+
await reader.cancel().catch(() => {
|
|
63
|
+
});
|
|
64
|
+
controller.error(fail());
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
async cancel(reason) {
|
|
68
|
+
await reader.cancel(reason);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return new Response(body, {
|
|
72
|
+
status: response.status,
|
|
73
|
+
statusText: response.statusText,
|
|
74
|
+
headers: response.headers
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/ai-sdk.ts
|
|
79
|
+
var Evidence = z.object({ openpond: z.record(z.string(), z.json()) }).passthrough();
|
|
80
|
+
function metadata(raw) {
|
|
81
|
+
const value = Evidence.safeParse(raw);
|
|
82
|
+
return value.success ? { openpond: value.data.openpond } : void 0;
|
|
83
|
+
}
|
|
84
|
+
function toAIModel(model) {
|
|
85
|
+
const connection = model.providerConnection();
|
|
86
|
+
const provider = createOpenAICompatible({
|
|
87
|
+
name: "openpond",
|
|
88
|
+
baseURL: productModelEndpoint(connection.baseUrl),
|
|
89
|
+
apiKey: connection.apiKey,
|
|
90
|
+
// The personalized gateway always supplies terminal usage and rejects
|
|
91
|
+
// OpenAI's optional stream_options field.
|
|
92
|
+
includeUsage: false,
|
|
93
|
+
headers: connection.teamId ? { "X-OpenPond-Team-Id": connection.teamId } : void 0,
|
|
94
|
+
transformRequestBody: (raw) => {
|
|
95
|
+
const body = JSON.parse(JSON.stringify(raw));
|
|
96
|
+
if (body.model !== model.id)
|
|
97
|
+
throw new ProductModelError("model_override_forbidden", 0, false);
|
|
98
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
99
|
+
"model",
|
|
100
|
+
"messages",
|
|
101
|
+
"stream",
|
|
102
|
+
"max_tokens",
|
|
103
|
+
"temperature",
|
|
104
|
+
"top_p",
|
|
105
|
+
"seed",
|
|
106
|
+
"stop",
|
|
107
|
+
"tools",
|
|
108
|
+
"tool_choice"
|
|
109
|
+
]);
|
|
110
|
+
if (Object.keys(body).some((key) => !allowed.has(key)))
|
|
111
|
+
throw new ProductModelError("parameter_unsupported", 0, false);
|
|
112
|
+
return body;
|
|
113
|
+
},
|
|
114
|
+
metadataExtractor: {
|
|
115
|
+
async extractMetadata({ parsedBody }) {
|
|
116
|
+
return metadata(parsedBody);
|
|
117
|
+
},
|
|
118
|
+
createStreamExtractor() {
|
|
119
|
+
let evidence;
|
|
120
|
+
return {
|
|
121
|
+
processChunk(chunk) {
|
|
122
|
+
evidence = metadata(chunk) ?? evidence;
|
|
123
|
+
},
|
|
124
|
+
buildMetadata() {
|
|
125
|
+
return evidence;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
supportsStructuredOutputs: model.capabilities.structuredOutput,
|
|
131
|
+
fetch: async (url, init) => {
|
|
132
|
+
const timeout = AbortSignal.timeout(connection.timeoutMs ?? 6e4);
|
|
133
|
+
const headers = new Headers(init?.headers);
|
|
134
|
+
headers.set("Authorization", `Bearer ${connection.apiKey}`);
|
|
135
|
+
if (connection.teamId) headers.set("X-OpenPond-Team-Id", connection.teamId);
|
|
136
|
+
return boundProductModelResponse(
|
|
137
|
+
await (connection.fetch ?? fetch)(url, {
|
|
138
|
+
...init,
|
|
139
|
+
redirect: "error",
|
|
140
|
+
headers,
|
|
141
|
+
signal: init?.signal ? AbortSignal.any([init.signal, timeout]) : timeout
|
|
142
|
+
})
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}).chatModel(model.id);
|
|
146
|
+
const check = (options) => {
|
|
147
|
+
const rawOptions = options.providerOptions?.openpond;
|
|
148
|
+
if (rawOptions && ["stream", "stream_options"].some((key) => key in rawOptions))
|
|
149
|
+
throw new ProductModelError("transport_override_forbidden", 0, false);
|
|
150
|
+
if (options.tools?.length && !model.capabilities.tools)
|
|
151
|
+
throw new ProductModelError("tools_unsupported", 0, false);
|
|
152
|
+
if (options.responseFormat?.type === "json")
|
|
153
|
+
throw new ProductModelError("structured_output_unsupported", 0, false);
|
|
154
|
+
if (options.prompt.some(
|
|
155
|
+
(message) => message.role === "tool" && !model.capabilities.tools || message.role !== "system" && message.content.some(
|
|
156
|
+
(part) => part.type !== "text" && !(model.capabilities.tools && ["tool-call", "tool-result"].includes(part.type))
|
|
157
|
+
)
|
|
158
|
+
))
|
|
159
|
+
throw new ProductModelError("input_capability_unsupported", 0, false);
|
|
160
|
+
};
|
|
161
|
+
const normalizeError = (error) => {
|
|
162
|
+
if (error instanceof ProductModelError) return error;
|
|
163
|
+
if (error && typeof error === "object" && "cause" in error && error.cause instanceof ProductModelError)
|
|
164
|
+
return error.cause;
|
|
165
|
+
const status = error && typeof error === "object" && "statusCode" in error && typeof error.statusCode === "number" ? error.statusCode : 0;
|
|
166
|
+
const unknown = status === 0 || status === 408 || status >= 500 || status >= 200 && status < 300;
|
|
167
|
+
return new ProductModelError(
|
|
168
|
+
status >= 400 ? "model_request_rejected" : "model_outcome_unknown",
|
|
169
|
+
status,
|
|
170
|
+
unknown
|
|
171
|
+
);
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
specificationVersion: provider.specificationVersion,
|
|
175
|
+
provider: provider.provider,
|
|
176
|
+
modelId: provider.modelId,
|
|
177
|
+
supportedUrls: provider.supportedUrls,
|
|
178
|
+
async doGenerate(options) {
|
|
179
|
+
check(options);
|
|
180
|
+
try {
|
|
181
|
+
return await provider.doGenerate(options);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
throw normalizeError(error);
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
async doStream(options) {
|
|
187
|
+
check(options);
|
|
188
|
+
if (!model.capabilities.streaming)
|
|
189
|
+
throw new ProductModelError("streaming_unsupported", 0, false);
|
|
190
|
+
try {
|
|
191
|
+
const result = await provider.doStream(options);
|
|
192
|
+
const reader = result.stream.getReader();
|
|
193
|
+
return {
|
|
194
|
+
...result,
|
|
195
|
+
stream: new ReadableStream({
|
|
196
|
+
async pull(controller) {
|
|
197
|
+
try {
|
|
198
|
+
const { done, value } = await reader.read();
|
|
199
|
+
if (done) {
|
|
200
|
+
controller.close();
|
|
201
|
+
reader.releaseLock();
|
|
202
|
+
} else
|
|
203
|
+
controller.enqueue(
|
|
204
|
+
value.type === "error" ? { ...value, error: normalizeError(value.error) } : value
|
|
205
|
+
);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
controller.error(normalizeError(error));
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
async cancel(reason) {
|
|
211
|
+
await reader.cancel(reason);
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
};
|
|
215
|
+
} catch (error) {
|
|
216
|
+
throw normalizeError(error);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
export {
|
|
222
|
+
toAIModel
|
|
223
|
+
};
|
|
224
|
+
//# sourceMappingURL=ai-sdk.js.map
|