runbios-sdk 0.2.22 → 0.3.0-dev.323
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/CHANGELOG.md +53 -0
- package/README.md +74 -1
- package/dist/client.d.ts +11 -0
- package/dist/client.js +29 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/resources/inference.d.ts +74 -2
- package/dist/resources/inference.js +73 -5
- package/dist/resources/loop.d.ts +95 -71
- package/dist/resources/loop.js +168 -18
- package/dist/resources/training.js +12 -2
- package/dist/types.d.ts +283 -69
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
`runbios-sdk` on npm. Versions are 0.x semver: the minor moves when a public
|
|
4
|
+
surface is added, renamed or removed, the patch for a fix inside an unchanged
|
|
5
|
+
surface. The SDK, the Python SDK (`runbios-sdk` on PyPI) and the MCP server
|
|
6
|
+
(`runbios-mcp`) are released together, at one version.
|
|
7
|
+
|
|
8
|
+
## 0.3.0
|
|
9
|
+
|
|
10
|
+
Added
|
|
11
|
+
|
|
12
|
+
- `training.create` and `training.preflight` take `startFrom.exportStep`
|
|
13
|
+
(`'best'`, the default, or `'final'`): which step of a job trained on top of
|
|
14
|
+
earlier versions its merged model is made of. It needs `exportMerged: true`;
|
|
15
|
+
without it, or with any other value, the SDK refuses before sending, as the
|
|
16
|
+
API does. `TrainingStartFrom` echoes `export_step`.
|
|
17
|
+
- Conscious Loop: `inference` chat and messages calls take
|
|
18
|
+
`loop: { pipeline, onSampleId }`, which sends `X-Loop-Pipeline` and hands
|
|
19
|
+
back the sample id (`loop_sample_id`, or to `onSampleId` before the first
|
|
20
|
+
streamed chunk).
|
|
21
|
+
- Conscious Loop: `loop.listPipelineAttempts` pages a pipeline's attempts;
|
|
22
|
+
`loop.listPipelines` takes `limit`, `q` and `status`; a pipeline's training
|
|
23
|
+
settings (`training`, three-state on a write) and `training_defaults`;
|
|
24
|
+
`loop.listModels` also lists the models not offered, with why;
|
|
25
|
+
`loop.getBenchmarkHistory` takes `pipeline`; `loop.listTraces` takes `q`.
|
|
26
|
+
- Feedback (`signal`, `upvote`, `downvote`, `correct`, `gold`) is sent again
|
|
27
|
+
after `Retry-After` while its sample is still being recorded
|
|
28
|
+
(`409 SAMPLE_NOT_YET_RECORDED`), at most 6 tries; `ApiError` carries
|
|
29
|
+
`retryAfter`.
|
|
30
|
+
|
|
31
|
+
Changed
|
|
32
|
+
|
|
33
|
+
- `LoopSignalParams` takes a person's feedback only: `verdict` is
|
|
34
|
+
`LoopFeedbackVerdict` (`accepted`, `rejected`, `edited` or `gold`) and
|
|
35
|
+
`source` is `'human'`; `score` is gone. The API refuses a `scored` verdict,
|
|
36
|
+
any other source, and a `score` on any verdict with 400.
|
|
37
|
+
- A merge-chain attempt replays about half of what it trains on (was a fifth),
|
|
38
|
+
and a version built on one that learned a deleted conversation says so in
|
|
39
|
+
its lineage.
|
|
40
|
+
|
|
41
|
+
Removed
|
|
42
|
+
|
|
43
|
+
- `Pipeline.champion_version` (use `live_version`) and
|
|
44
|
+
`PipelineVersion.is_champion` (use `is_live`), `benchmark_id` and
|
|
45
|
+
`benchmark_name` (use `listPipelineBenchmarks`), `benchmark_min_delta` (the
|
|
46
|
+
least rise a benchmark must show is the platform's; each measurement's
|
|
47
|
+
`reason` in `promotion` says whether it was met), `auto_promote` (use
|
|
48
|
+
`promotion.policy`), and `monthly_ceiling_cents` and `month_spent_cents`:
|
|
49
|
+
the API no longer sends them. `TrainingRuleMonthlyLimitRefusal` keeps
|
|
50
|
+
`error` and `resumes_at`. The monthly safety limit's figures are not fields
|
|
51
|
+
of any answer, and no sentence states them either: `next_reason` and the
|
|
52
|
+
`MONTHLY_LIMIT_REACHED` message say the limit holds the pipeline and when
|
|
53
|
+
the next attempt can start, with no amount.
|
package/README.md
CHANGED
|
@@ -100,7 +100,7 @@ without an explicit replay acknowledgement from the endpoint.
|
|
|
100
100
|
|
|
101
101
|
**Parameter naming on `/v1` calls.** Only the parameters this SDK names are
|
|
102
102
|
camelCase: `model`, `messages`, `tools`, `toolChoice`, `reasoningEffort`,
|
|
103
|
-
`inferenceKey`, `idempotencyKey`, `requestId`, `signal`. Every other key is
|
|
103
|
+
`inferenceKey`, `idempotencyKey`, `requestId`, `signal`, `loop`. Every other key is
|
|
104
104
|
forwarded to the OpenAI-compatible endpoint **verbatim**, so it must use
|
|
105
105
|
OpenAI's own snake_case spelling — `max_tokens`, `top_p`, `stop`,
|
|
106
106
|
`frequency_penalty`, `presence_penalty`. The endpoint rejects an unknown key
|
|
@@ -217,6 +217,79 @@ for await (const event of dedicated.streamCompletions({ model: 'completion-deplo
|
|
|
217
217
|
methods close the upstream reader when iteration ends. None of the inference
|
|
218
218
|
POSTs are retried automatically, and `idempotencyKey` is sent only if supplied.
|
|
219
219
|
|
|
220
|
+
#### Record conversations into a Conscious Loop pipeline (one header)
|
|
221
|
+
|
|
222
|
+
Add `loop: { pipeline: '<tag>' }` to `chatCompletions`, `streamChatCompletions`,
|
|
223
|
+
`messages` or `streamMessages` and the call is sent exactly as before with one
|
|
224
|
+
more header, `X-Loop-Pipeline: <tag>` (several tags: `pipeline: ['a', 'b']`).
|
|
225
|
+
The gateway records the exchange -- what was asked, what the model answered and
|
|
226
|
+
its tool calls -- as a sample of the pipelines those tags feed (a pipeline's
|
|
227
|
+
`own_tag`), even where recording is off for that model: the header is your
|
|
228
|
+
consent for that one exchange. With `chatCompletions` and `streamChatCompletions`
|
|
229
|
+
it works for serverless models and dedicated deployments, including the
|
|
230
|
+
pipeline's own `loop-<tag>` model. `messages` and `streamMessages` (the
|
|
231
|
+
Anthropic shape) work for serverless models; a dedicated deployment, and so a
|
|
232
|
+
pipeline's `loop-<tag>` model, serves the OpenAI shape and refuses
|
|
233
|
+
`/v1/messages` with 400, so call it with `chatCompletions`. Only the tags of a
|
|
234
|
+
pipeline that exists are recorded: a call naming a misspelt tag, or a deleted
|
|
235
|
+
pipeline's, is not recorded under it, and `client.loop.stats()` lists such
|
|
236
|
+
calls under `capture`.
|
|
237
|
+
|
|
238
|
+
The gateway answers with the sample's id in `X-Loop-Sample-Id`. A non-streaming
|
|
239
|
+
result carries it as `loop_sample_id` (non-enumerable: it is never part of the
|
|
240
|
+
answer's JSON), and `loop.onSampleId` hears it as soon as the headers arrive,
|
|
241
|
+
before the first streamed chunk. It is `null` when the gateway recorded nothing.
|
|
242
|
+
Send feedback on that id; it is the trace id the feedback calls take.
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const reply = await client.inference.chatCompletions({
|
|
246
|
+
model: 'loop-support-replies',
|
|
247
|
+
messages: [{ role: 'user', content: 'Where is my order?' }],
|
|
248
|
+
loop: { pipeline: 'support-replies' },
|
|
249
|
+
});
|
|
250
|
+
const sampleId = reply.loop_sample_id;
|
|
251
|
+
if (sampleId) {
|
|
252
|
+
await client.loop.upvote(sampleId); // a good answer
|
|
253
|
+
// await client.loop.downvote(sampleId); // a bad one
|
|
254
|
+
// await client.loop.correct(sampleId, 'The right answer.');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
for await (const chunk of client.inference.streamChatCompletions({
|
|
258
|
+
model: 'loop-support-replies',
|
|
259
|
+
messages: [{ role: 'user', content: 'Where is my order?' }],
|
|
260
|
+
loop: { pipeline: 'support-replies', onSampleId: (id) => console.log('sample', id) },
|
|
261
|
+
})) {
|
|
262
|
+
process.stdout.write(String((chunk.choices as any)?.[0]?.delta?.content ?? ''));
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Feedback sent before the sample is recorded is refused `409
|
|
267
|
+
SAMPLE_NOT_YET_RECORDED`, and nothing is stored. `upvote`, `downvote`,
|
|
268
|
+
`correct`, `gold` and `signal` send it again after the `Retry-After` seconds (2
|
|
269
|
+
when none is given), at most 6 times in all, then throw that error. A stream
|
|
270
|
+
that was cut short is never recorded, so its id stays unknown (it is counted in
|
|
271
|
+
`client.loop.stats()` under `capture`). If you send
|
|
272
|
+
`X-Request-ID`, keep it unique per call: a repeated one is recorded once.
|
|
273
|
+
`completions`, `embeddings` and `rerank` are not recorded and refuse the option.
|
|
274
|
+
|
|
275
|
+
Many feedback items at once: `signalMany` sends each as `signal` does, the same
|
|
276
|
+
retry included, with up to 8 in flight (`{ concurrency }`, at most 32), and
|
|
277
|
+
returns every item in order with its `signal` or its `error`:
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
const results = await client.loop.signalMany([
|
|
281
|
+
{ trace_id: idA, verdict: 'good' },
|
|
282
|
+
{ trace_id: idB, verdict: 'corrected', correction: 'Refunds take five working days.' },
|
|
283
|
+
]);
|
|
284
|
+
const failed = results.filter((r) => r.error);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
Sending a sample yourself with `client.loop.capture` and already know the
|
|
288
|
+
verdict? Pass `feedback: { verdict: 'good' | 'bad' | 'corrected', correction }`
|
|
289
|
+
and it is written with the sample, in one transaction: both are stored or
|
|
290
|
+
neither is. `good`, `bad` and `corrected` work in `signal` and `signalMany` too,
|
|
291
|
+
stored as `accepted`, `rejected` and `edited`.
|
|
292
|
+
|
|
220
293
|
#### Workspace serverless usage and limits (read-only)
|
|
221
294
|
|
|
222
295
|
A workspace-bound platform key or hosted OAuth grant with `analytics:read` can
|
package/dist/client.d.ts
CHANGED
|
@@ -50,8 +50,19 @@ export declare class ApiError extends Error {
|
|
|
50
50
|
readonly availableGpus: AvailableGpuAlternative[] | undefined;
|
|
51
51
|
/** Availability snapshot time behind an availability rejection. */
|
|
52
52
|
readonly checkedAt: string | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* How many seconds the service asked you to wait before trying again: its
|
|
55
|
+
* `Retry-After` header, as seconds or as a date. Undefined when it sent none.
|
|
56
|
+
*/
|
|
57
|
+
retryAfter: number | undefined;
|
|
53
58
|
constructor(status: number, body: ApiErrorBody);
|
|
54
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Seconds to wait from a `Retry-After` header: a number of seconds, or an HTTP
|
|
62
|
+
* date (seconds until then, never negative). Undefined when absent or
|
|
63
|
+
* unreadable. @internal
|
|
64
|
+
*/
|
|
65
|
+
export declare function retryAfterSeconds(value: string | null | undefined, now?: number): number | undefined;
|
|
55
66
|
/**
|
|
56
67
|
* The one transient GPU-rejection code: the GPU is not in stock right now, so
|
|
57
68
|
* waiting or joining the capacity queue can still succeed.
|
package/dist/client.js
CHANGED
|
@@ -53,6 +53,11 @@ export class ApiError extends Error {
|
|
|
53
53
|
availableGpus;
|
|
54
54
|
/** Availability snapshot time behind an availability rejection. */
|
|
55
55
|
checkedAt;
|
|
56
|
+
/**
|
|
57
|
+
* How many seconds the service asked you to wait before trying again: its
|
|
58
|
+
* `Retry-After` header, as seconds or as a date. Undefined when it sent none.
|
|
59
|
+
*/
|
|
60
|
+
retryAfter;
|
|
56
61
|
constructor(status, body) {
|
|
57
62
|
const err = body.error;
|
|
58
63
|
const nested = typeof err === 'object' && err !== null ? err : null;
|
|
@@ -71,8 +76,29 @@ export class ApiError extends Error {
|
|
|
71
76
|
const alts = flat.available_gpus ?? flat.available_alternatives;
|
|
72
77
|
this.availableGpus = Array.isArray(alts) && alts.length > 0 ? alts : undefined;
|
|
73
78
|
this.checkedAt = flat.checked_at;
|
|
79
|
+
this.retryAfter = undefined;
|
|
74
80
|
}
|
|
75
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Seconds to wait from a `Retry-After` header: a number of seconds, or an HTTP
|
|
84
|
+
* date (seconds until then, never negative). Undefined when absent or
|
|
85
|
+
* unreadable. @internal
|
|
86
|
+
*/
|
|
87
|
+
export function retryAfterSeconds(value, now = Date.now()) {
|
|
88
|
+
const raw = (value ?? '').trim();
|
|
89
|
+
if (!raw)
|
|
90
|
+
return undefined;
|
|
91
|
+
if (/^\d+(\.\d+)?$/.test(raw))
|
|
92
|
+
return Number(raw);
|
|
93
|
+
const at = Date.parse(raw);
|
|
94
|
+
return Number.isNaN(at) ? undefined : Math.max(0, (at - now) / 1000);
|
|
95
|
+
}
|
|
96
|
+
/** The error a non-2xx response stands for, with its `Retry-After`. @internal */
|
|
97
|
+
function errorFromResponse(res, data) {
|
|
98
|
+
const err = buildApiError(res.status, data);
|
|
99
|
+
err.retryAfter = retryAfterSeconds(res.headers.get('retry-after'));
|
|
100
|
+
return err;
|
|
101
|
+
}
|
|
76
102
|
/**
|
|
77
103
|
* Flatten the standard `{ error: { ... } }` envelope over the legacy top-level
|
|
78
104
|
* fields so structured capacity data is found in either shape. @internal
|
|
@@ -344,7 +370,7 @@ export class HttpClient {
|
|
|
344
370
|
constructor(config) {
|
|
345
371
|
// Default host stays api.runbios.ai for now; cutover to api.runbios.ai is
|
|
346
372
|
// planned once its DNS exists.
|
|
347
|
-
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api.runbios.ai').replace(/\/+$/, '');
|
|
373
|
+
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api-dev.runbios.ai').replace(/\/+$/, '');
|
|
348
374
|
this.apiKey = config.apiKey ?? envApiKey();
|
|
349
375
|
this.accessToken = config.accessToken;
|
|
350
376
|
this.orgId = config.orgId;
|
|
@@ -431,7 +457,7 @@ export class HttpClient {
|
|
|
431
457
|
}
|
|
432
458
|
const data = await res.json().catch(() => ({}));
|
|
433
459
|
if (!res.ok) {
|
|
434
|
-
throw
|
|
460
|
+
throw errorFromResponse(res, data);
|
|
435
461
|
}
|
|
436
462
|
return data;
|
|
437
463
|
}
|
|
@@ -486,7 +512,7 @@ export class HttpClient {
|
|
|
486
512
|
}
|
|
487
513
|
const data = await res.json().catch(() => ({}));
|
|
488
514
|
if (!res.ok) {
|
|
489
|
-
throw
|
|
515
|
+
throw errorFromResponse(res, data);
|
|
490
516
|
}
|
|
491
517
|
return data;
|
|
492
518
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ import { Loop } from './resources/loop.js';
|
|
|
36
36
|
* SDK version. Sent as part of the User-Agent header.
|
|
37
37
|
* Must match package.json "version" -- enforced by a contract test.
|
|
38
38
|
*/
|
|
39
|
-
export declare const VERSION = "0.
|
|
39
|
+
export declare const VERSION = "0.3.0-dev.323";
|
|
40
40
|
export declare class RunBiOS {
|
|
41
41
|
/** Search models, fetch configs, check adapter compatibility. */
|
|
42
42
|
readonly models: Models;
|
|
@@ -74,8 +74,8 @@ export { Integrations, type HuggingFaceIntegration, type IntegrationBrowseParams
|
|
|
74
74
|
export { Training } from './resources/training.js';
|
|
75
75
|
export { Wallet } from './resources/wallet.js';
|
|
76
76
|
export { GPU, type GPURecommendation } from './resources/gpu.js';
|
|
77
|
-
export { Inference, validateChatRequest, parseSSE, buildInferenceRequest, contextSizingBasis, type ContextSizingBasis, CONTEXT_DEFAULT_CEILING, CONTEXT_EDITABLE_FLOOR, type ChatMessage, type FunctionTool, type ChatCompletionParams, type ChatCompletionResponse, type ChatCompletionChunk, type InferenceVerbParams, type CompletionParams, type EmbeddingParams, type RerankParams, type AnthropicMessageParams, type ServerlessUsageWindow, type ServerlessTimeseriesMetric, type ServerlessWorkspaceLimits, type ServerlessUsageEnvelope, type ServerlessUsageOverviewResponse, type ServerlessUsageRowsResponse, type ServerlessUsageTimeseriesResponse, type ServerlessUsageDailyResponse, type ServerlessSavingsTotals, type ServerlessSavingsPeriod, type ServerlessUsageSavingsResponse, } from './resources/inference.js';
|
|
78
|
-
export type { BiOSConfig, PaginatedResponse, ApiErrorBody, AvailableGpuAlternative, CapacityMinimumRequirement, InferenceBookingAccepted, Model, ModelSearchParams, ModelSearchResponse, ModelDetailResponse, InferenceModel, InferenceModelListResponse, ModelConfig, Dataset, DatasetListParams, DatasetListResponse, DatasetUploadParams, DatasetPreview, DatasetPreviewParams, DatasetImportHFParams, DatasetRegisterHFParams, DatasetHubSearchParams, DatasetHubPreviewParams, DatasetValidation, DatasetFormatVariant, DatasetFormatSpec, DatasetFormatSpecs, DatasetStorageUsage, TrainingMethod, RLHFAlgorithm, AdapterType, TrainingJobStatus, TrainingStatusFilter, TrainingCreateParams, TrainingStartFromParams, TrainingStartFrom, TrainingListParams, TrainingListResponse, TrainingJob, TrainingMetrics, TrainingResourceMetrics, TrainingDeviceMetrics, MetricPoint, MetricGraphConfig, TrainingCheckpoint, TrainingLogs, TrainingLogEntry, TrainingStopResponse, TrainingResumeResponse, CanonicalTrainingRequest, TrainingPreflightDataset, TrainingPreflightWarning, TrainingPreflightResponse, TrainingCapabilityChoice, TrainingConfigFieldCapability, TrainingCapabilities, TrainingFeature, GPUChoice, WalletBalance, Transaction, TransactionListResponse, TransactionListParams, GPUInfo, GPUPricingResponse, GPUOptionsParams, TrainingRecommendParams, TrainingAdvisorVerdict, TrainingAdvisorRecommendation, TrainingAdvisorUserShape, TrainingAdvisorJustification, TrainingAdvisorBasis, GPUOption, GPUOptionSuggestion, GPUOptionsResponse, InferenceStatus, InferenceCreateParams, InferenceUpdateParams, InferenceDeployment, InferenceDeploymentSummary, InferenceCreateResponse, InferenceListResponse, InferenceUpdateResponse, InferenceLifecycleResponse, InferenceDeleteResponse, InferenceGPUOptionMarket, InferenceGPUOption, InferenceGPUAlternative, InferenceGPUOptionsParams, InferenceGPUOptionsResponse, InferenceCanonicalRequest, InferencePreflightWarning, InferencePreflightResponse, AdapterCompatibility, AdapterCompatibilityResponse, AdapterCompatibilityParams, SupportedArchitecture, ArchitectureScope, SupportedArchitecturesResponse, ApiKey, ApiKeyScope, ApiKeyIntrospection, Organization, OrgMember, OrgInvite, Workspace, Integration, IntegrationCreateParams, StorageUsage, StorageObject, PromptTokensDetails, ChatCompletionUsage, ServingKind, TrainingRulePausedReason, TrainingTrigger, TrainingRecipe, TrainingRunState, TrainingVerdict, TrainingDecision, EvaluationStatus, EvaluationItemStatus, ItemErrorCode, EvaluationWinner, EvaluationItemWinnerFilter, ReportStatusFilter, ReportSort, EvaluationWarningCode, TrainingToolCall, TrainingMessage, TrainingRuleServing, TrainingRunPromoteRequest, TrainingRunRejectRequest, TrainingRunRollbackRequest, TrainingRunCancelRequest, TrainingRunListParams, TrainingRun, TrainingRunSummary, TrainingRunEvent, TrainingRunLinks, TrainingRunActions, EvaluationRef, EvaluationDecoding, EvaluationJudgeDimension, EvaluationDimensionScore, EvaluationWarning, EvaluationMargin, Evaluation, EvaluationItemJudge, EvaluationReferenceKind, EvaluationReference, EvaluationPairwiseOrder, EvaluationItemPairwise, EvaluationItemSide, EvaluationItem, EvaluationItemListParams, BenchmarkStatus, BenchmarkSourceKind, BenchmarkRunStatus, BenchmarkScoring, BenchmarkCheck, BenchmarkDimensionScore, Benchmark, BenchmarkItem, BenchmarkRun, BenchmarkRunItemSide, BenchmarkRunItem, BenchmarkRunItemListParams, BenchmarkHistoryPoint, BenchmarkSource, BenchmarkCreateParams, BenchmarkListParams, BenchmarkItemListParams, BenchmarkHistoryParams, BenchmarkRetireParams, InferenceAlias, InferenceAliasRequest, TrainingRuleDeleteResponse, TrainingRunListResponse, TrainingRunBenchmarkRun, TrainingRunResponse, TrainingRunActionResponse, EvaluationItemsResponse, BenchmarkRunItemsResponse, InferenceAliasListResponse, InferenceAliasResponse, InferenceAliasDeleteResponse, BenchmarkListResponse, BenchmarkItemsResponse, BenchmarkHistoryResponse, TrainingRuleRunRefusalCode, TrainingRuleSaveRefusalCode, TrainingRuleMonthlyLimitRefusal, PipelineStatus, PipelineActiveRun, PipelineVersion, PipelineAttempt, PipelineAttemptTrigger, Lineage, PipelineChain, PipelineChainRebuild, TrainingRunRollbackRefusalCode, LoopTraceRebuild, LoopTraceDeleteResponse, PipelineAttemptOutcome, PipelineLiveDeploymentStatus, PipelineLiveDeployment, LoopStorageWhere, LoopStorage, Pipeline, PipelineListParams, PipelineListResponse, PipelineResponse, PipelineTrainWhen, PipelineSchedule, PipelineWriteRequest, PipelineMutationResponse, PipelineImportParams, PipelineFloors, HeldBackFloors, LoopModel, LoopModelsResponse, LoopTracePipeline, LoopTracePipelinesResponse, LoopMetricsHistoryParams, LoopMetricPoint, LoopSignalDay, LoopMetricsHistory, PromotionPolicyState, RuleBenchmark, PipelineBenchmarksResponse, PipelineBenchmarkAttachParams, PipelineBenchmarkMoveParams, CompareBenchmark, VersionBenchmarkScore, PromotionMeasurement, PromotionOutcome, VersionHoldout, VersionScores, VersionScoreDifference, VersionCompareResponse, VersionCompareParams, TrainingRunPromotion, VerdictCheck, VerdictCheckKey, VerdictChecksWithheld, TrainingRunCost, AttemptDirection, AttemptHoldout, AttemptCompareSide, AttemptSharedSample, AttemptSharedFigures, AttemptBenchmarkSide, AttemptCompareBenchmark, AttemptCompareItemSide, AttemptCompareItem, AttemptCompareResponse, AttemptCompareParams, } from './types.js';
|
|
77
|
+
export { Inference, validateChatRequest, parseSSE, buildInferenceRequest, contextSizingBasis, type ContextSizingBasis, CONTEXT_DEFAULT_CEILING, CONTEXT_EDITABLE_FLOOR, type ChatMessage, type FunctionTool, type ChatCompletionParams, type ChatCompletionResponse, type ChatCompletionChunk, type InferenceVerbParams, type CompletionParams, type EmbeddingParams, type RerankParams, type AnthropicMessageParams, type AnthropicMessageResponse, type LoopCaptureOption, type LoopSampleIdOnResult, LOOP_PIPELINE_HEADER, LOOP_SAMPLE_ID_HEADER, type ServerlessUsageWindow, type ServerlessTimeseriesMetric, type ServerlessWorkspaceLimits, type ServerlessUsageEnvelope, type ServerlessUsageOverviewResponse, type ServerlessUsageRowsResponse, type ServerlessUsageTimeseriesResponse, type ServerlessUsageDailyResponse, type ServerlessSavingsTotals, type ServerlessSavingsPeriod, type ServerlessUsageSavingsResponse, } from './resources/inference.js';
|
|
78
|
+
export type { BiOSConfig, PaginatedResponse, ApiErrorBody, AvailableGpuAlternative, CapacityMinimumRequirement, InferenceBookingAccepted, Model, ModelSearchParams, ModelSearchResponse, ModelDetailResponse, InferenceModel, InferenceModelListResponse, ModelConfig, Dataset, DatasetListParams, DatasetListResponse, DatasetUploadParams, DatasetPreview, DatasetPreviewParams, DatasetImportHFParams, DatasetRegisterHFParams, DatasetHubSearchParams, DatasetHubPreviewParams, DatasetValidation, DatasetFormatVariant, DatasetFormatSpec, DatasetFormatSpecs, DatasetStorageUsage, TrainingMethod, RLHFAlgorithm, AdapterType, TrainingJobStatus, TrainingStatusFilter, TrainingCreateParams, TrainingStartFromParams, TrainingStartFrom, TrainingExportStep, TrainingListParams, TrainingListResponse, TrainingJob, TrainingMetrics, TrainingResourceMetrics, TrainingDeviceMetrics, MetricPoint, MetricGraphConfig, TrainingCheckpoint, TrainingLogs, TrainingLogEntry, TrainingStopResponse, TrainingResumeResponse, CanonicalTrainingRequest, TrainingPreflightDataset, TrainingPreflightWarning, TrainingPreflightResponse, TrainingCapabilityChoice, TrainingConfigFieldCapability, TrainingCapabilities, TrainingFeature, GPUChoice, WalletBalance, Transaction, TransactionListResponse, TransactionListParams, GPUInfo, GPUPricingResponse, GPUOptionsParams, TrainingRecommendParams, TrainingAdvisorVerdict, TrainingAdvisorRecommendation, TrainingAdvisorUserShape, TrainingAdvisorJustification, TrainingAdvisorBasis, GPUOption, GPUOptionSuggestion, GPUOptionsResponse, InferenceStatus, InferenceCreateParams, InferenceUpdateParams, InferenceDeployment, InferenceDeploymentSummary, InferenceCreateResponse, InferenceListResponse, InferenceUpdateResponse, InferenceLifecycleResponse, InferenceDeleteResponse, InferenceGPUOptionMarket, InferenceGPUOption, InferenceGPUAlternative, InferenceGPUOptionsParams, InferenceGPUOptionsResponse, InferenceCanonicalRequest, InferencePreflightWarning, InferencePreflightResponse, AdapterCompatibility, AdapterCompatibilityResponse, AdapterCompatibilityParams, SupportedArchitecture, ArchitectureScope, SupportedArchitecturesResponse, ApiKey, ApiKeyScope, ApiKeyIntrospection, Organization, OrgMember, OrgInvite, Workspace, Integration, IntegrationCreateParams, StorageUsage, StorageObject, PromptTokensDetails, ChatCompletionUsage, ServingKind, TrainingRulePausedReason, TrainingTrigger, TrainingRecipe, TrainingRunState, TrainingVerdict, TrainingDecision, EvaluationStatus, EvaluationItemStatus, ItemErrorCode, EvaluationWinner, EvaluationItemWinnerFilter, ReportStatusFilter, ReportSort, EvaluationWarningCode, TrainingToolCall, TrainingMessage, TrainingRuleServing, TrainingRunPromoteRequest, TrainingRunRejectRequest, TrainingRunRollbackRequest, TrainingRunCancelRequest, TrainingRunListParams, TrainingRun, TrainingRunSummary, TrainingRunEvent, TrainingRunLinks, TrainingRunActions, EvaluationRef, EvaluationDecoding, EvaluationJudgeDimension, EvaluationDimensionScore, EvaluationWarning, EvaluationMargin, Evaluation, EvaluationItemJudge, EvaluationReferenceKind, EvaluationReference, EvaluationPairwiseOrder, EvaluationItemPairwise, EvaluationItemSide, EvaluationItem, EvaluationItemListParams, BenchmarkStatus, BenchmarkSourceKind, BenchmarkRunStatus, BenchmarkScoring, BenchmarkCheck, BenchmarkDimensionScore, Benchmark, BenchmarkItem, BenchmarkRun, BenchmarkRunItemSide, BenchmarkRunItem, BenchmarkRunItemListParams, BenchmarkHistoryPoint, BenchmarkSource, BenchmarkCreateParams, BenchmarkListParams, BenchmarkItemListParams, BenchmarkHistoryParams, BenchmarkRetireParams, InferenceAlias, InferenceAliasRequest, TrainingRuleDeleteResponse, TrainingRunListResponse, TrainingRunBenchmarkRun, TrainingRunResponse, TrainingRunActionResponse, EvaluationItemsResponse, BenchmarkRunItemsResponse, InferenceAliasListResponse, InferenceAliasResponse, InferenceAliasDeleteResponse, BenchmarkListResponse, BenchmarkItemsResponse, BenchmarkHistoryResponse, TrainingRuleRunRefusalCode, TrainingRuleSaveRefusalCode, TrainingRuleMonthlyLimitRefusal, PipelineStatus, PipelineActiveRun, PipelineVersion, PipelineAttempt, AttemptExplanation, PipelineAttemptListParams, PipelineAttemptListResponse, PipelineAttemptModelFilter, PipelineAttemptResultFilter, PipelineAttemptTrigger, Lineage, PipelineChain, PipelineChainRebuild, TrainingRunRollbackRefusalCode, LoopTraceRebuild, LoopTraceDeleteResponse, PipelineAttemptOutcome, PipelineLiveDeploymentStatus, PipelineLiveDeployment, LoopStorageWhere, LoopStorage, Pipeline, PipelineListParams, PipelineListStatus, PipelineListResponse, PipelineTrainingSettings, PipelineTrainingDefaults, PipelineTrainingRequest, PipelineResponse, PipelineTrainWhen, PipelineSchedule, PipelineWriteRequest, PipelineMutationResponse, PipelineImportParams, PipelineFloors, HeldBackFloors, LoopModel, LoopModelsResponse, LoopUnavailableModel, LoopUnavailableReasonCode, LoopTracePipeline, LoopTracePipelinesResponse, LoopMetricsHistoryParams, LoopMetricPoint, LoopSignalDay, LoopMetricsHistory, LoopCaptureFeedback, LoopFeedbackWord, LoopCaptureHealth, LoopCaptureMiss, LoopSignalManyItem, LoopSignalManyResult, PromotionPolicyState, RuleBenchmark, PipelineBenchmarksResponse, PipelineBenchmarkAttachParams, PipelineBenchmarkMoveParams, CompareBenchmark, VersionBenchmarkScore, PromotionMeasurement, PromotionOutcome, VersionHoldout, VersionScores, VersionScoreDifference, VersionCompareResponse, VersionCompareParams, TrainingRunPromotion, VerdictCheck, VerdictCheckKey, VerdictChecksWithheld, TrainingRunCost, AttemptDirection, AttemptHoldout, AttemptCompareSide, AttemptSharedSample, AttemptSharedFigures, AttemptBenchmarkSide, AttemptCompareBenchmark, AttemptCompareItemSide, AttemptCompareItem, AttemptCompareResponse, AttemptCompareParams, } from './types.js';
|
|
79
79
|
/** The closed set of run states a training run never leaves. */
|
|
80
80
|
export { TERMINAL_RUN_STATES } from './types.js';
|
|
81
81
|
/** The most versions a pipeline may be set to make. */
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ import { Loop } from './resources/loop.js';
|
|
|
36
36
|
* SDK version. Sent as part of the User-Agent header.
|
|
37
37
|
* Must match package.json "version" -- enforced by a contract test.
|
|
38
38
|
*/
|
|
39
|
-
export const VERSION = '0.
|
|
39
|
+
export const VERSION = '0.3.0-dev.323';
|
|
40
40
|
export class RunBiOS {
|
|
41
41
|
/** Search models, fetch configs, check adapter compatibility. */
|
|
42
42
|
models;
|
|
@@ -102,7 +102,7 @@ export { Integrations } from './resources/integrations.js';
|
|
|
102
102
|
export { Training } from './resources/training.js';
|
|
103
103
|
export { Wallet } from './resources/wallet.js';
|
|
104
104
|
export { GPU } from './resources/gpu.js';
|
|
105
|
-
export { Inference, validateChatRequest, parseSSE, buildInferenceRequest, contextSizingBasis, CONTEXT_DEFAULT_CEILING, CONTEXT_EDITABLE_FLOOR, } from './resources/inference.js';
|
|
105
|
+
export { Inference, validateChatRequest, parseSSE, buildInferenceRequest, contextSizingBasis, CONTEXT_DEFAULT_CEILING, CONTEXT_EDITABLE_FLOOR, LOOP_PIPELINE_HEADER, LOOP_SAMPLE_ID_HEADER, } from './resources/inference.js';
|
|
106
106
|
/** The closed set of run states a training run never leaves. */
|
|
107
107
|
export { TERMINAL_RUN_STATES } from './types.js';
|
|
108
108
|
/** The most versions a pipeline may be set to make. */
|
|
@@ -49,6 +49,54 @@ export interface FunctionTool extends Record<string, unknown> {
|
|
|
49
49
|
strict?: boolean;
|
|
50
50
|
};
|
|
51
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Record one exchange into Conscious Loop pipelines with one header (the
|
|
54
|
+
* `loop` option of `chatCompletions`, `streamChatCompletions`, `messages` and
|
|
55
|
+
* `streamMessages`).
|
|
56
|
+
*
|
|
57
|
+
* The call goes to your model exactly as it would without it, with
|
|
58
|
+
* `X-Loop-Pipeline: <tag>[, <tag>...]` added: the gateway records the
|
|
59
|
+
* exchange (what was asked, what the model answered, its tool calls) as a
|
|
60
|
+
* sample of the pipelines those tags feed -- a pipeline's `own_tag` -- even
|
|
61
|
+
* where recording is off for that model, because the header is your consent
|
|
62
|
+
* for this one exchange. It answers with the sample's id in
|
|
63
|
+
* `X-Loop-Sample-Id`, before the body when streaming. Send feedback on that id
|
|
64
|
+
* with `client.loop.upvote`, `downvote` or `correct`: the sample id is the
|
|
65
|
+
* `traceId` they take. Feedback sent before the sample is recorded can be
|
|
66
|
+
* refused 409 `SAMPLE_NOT_YET_RECORDED`; send it again after the
|
|
67
|
+
* `Retry-After` seconds.
|
|
68
|
+
*
|
|
69
|
+
* Works on `/v1/chat/completions` for serverless models and dedicated
|
|
70
|
+
* deployments, a pipeline's own `loop-<tag>` model included, and on
|
|
71
|
+
* `/v1/messages` for serverless models: a dedicated deployment serves the
|
|
72
|
+
* OpenAI shape and refuses an Anthropic body with 400. Only the tags of a
|
|
73
|
+
* pipeline that exists are recorded; `client.loop.stats()` lists the calls
|
|
74
|
+
* that were not, under `capture`.
|
|
75
|
+
*/
|
|
76
|
+
export interface LoopCaptureOption {
|
|
77
|
+
/** One pipeline tag, or several; each a non-empty tag with no comma. */
|
|
78
|
+
pipeline: string | string[];
|
|
79
|
+
/**
|
|
80
|
+
* Called once with the sample id as soon as the response headers arrive
|
|
81
|
+
* (before the first streamed chunk), or with null when the gateway recorded
|
|
82
|
+
* nothing for this exchange (no `X-Loop-Sample-Id` came back). A
|
|
83
|
+
* non-streaming result also carries it as `loop_sample_id`.
|
|
84
|
+
*/
|
|
85
|
+
onSampleId?: (sampleId: string | null) => void;
|
|
86
|
+
}
|
|
87
|
+
/** The request header the `loop` option sends. */
|
|
88
|
+
export declare const LOOP_PIPELINE_HEADER = "X-Loop-Pipeline";
|
|
89
|
+
/** The response header the gateway answers a recorded exchange with: the sample's id. */
|
|
90
|
+
export declare const LOOP_SAMPLE_ID_HEADER = "X-Loop-Sample-Id";
|
|
91
|
+
/**
|
|
92
|
+
* A non-streaming `/v1` answer, as the endpoint sent it, plus
|
|
93
|
+
* `loop_sample_id`: the `X-Loop-Sample-Id` the gateway answered with, or null
|
|
94
|
+
* when it sent none. It is NON-ENUMERABLE, so it is never part of the
|
|
95
|
+
* object's JSON or its keys; read it by name.
|
|
96
|
+
*/
|
|
97
|
+
export interface LoopSampleIdOnResult {
|
|
98
|
+
readonly loop_sample_id?: string | null;
|
|
99
|
+
}
|
|
52
100
|
export interface ChatCompletionParams extends Record<string, unknown> {
|
|
53
101
|
messages: ChatMessage[];
|
|
54
102
|
model?: string;
|
|
@@ -75,8 +123,10 @@ export interface ChatCompletionParams extends Record<string, unknown> {
|
|
|
75
123
|
idempotencyKey?: string;
|
|
76
124
|
requestId?: string;
|
|
77
125
|
signal?: AbortSignal;
|
|
126
|
+
/** Record this exchange into Conscious Loop pipelines. See {@link LoopCaptureOption}. */
|
|
127
|
+
loop?: LoopCaptureOption;
|
|
78
128
|
}
|
|
79
|
-
export type ChatCompletionResponse = Record<string, unknown
|
|
129
|
+
export type ChatCompletionResponse = Record<string, unknown> & LoopSampleIdOnResult;
|
|
80
130
|
export type ChatCompletionChunk = Record<string, unknown>;
|
|
81
131
|
export interface InferenceVerbParams extends Record<string, unknown> {
|
|
82
132
|
model?: string;
|
|
@@ -99,7 +149,11 @@ export interface AnthropicMessageParams extends InferenceVerbParams {
|
|
|
99
149
|
messages: Array<Record<string, unknown>>;
|
|
100
150
|
max_tokens: number;
|
|
101
151
|
anthropicVersion?: string;
|
|
152
|
+
/** Record this exchange into Conscious Loop pipelines. See {@link LoopCaptureOption}. */
|
|
153
|
+
loop?: LoopCaptureOption;
|
|
102
154
|
}
|
|
155
|
+
/** A non-streaming `/v1/messages` answer, with `loop_sample_id` (see {@link LoopSampleIdOnResult}). */
|
|
156
|
+
export type AnthropicMessageResponse = Record<string, unknown> & LoopSampleIdOnResult;
|
|
103
157
|
export type ServerlessUsageWindow = '1h' | '24h' | '7d' | '30d' | '90d';
|
|
104
158
|
export type ServerlessTimeseriesMetric = 'requests' | 'tokens' | 'spend' | 'ttft_p50' | 'ttft_p95' | 'tps';
|
|
105
159
|
export interface ServerlessWorkspaceLimits {
|
|
@@ -359,12 +413,30 @@ export declare class Inference {
|
|
|
359
413
|
completions(params: CompletionParams): Promise<Record<string, unknown>>;
|
|
360
414
|
embeddings(params: EmbeddingParams): Promise<Record<string, unknown>>;
|
|
361
415
|
rerank(params: RerankParams): Promise<Record<string, unknown>>;
|
|
362
|
-
|
|
416
|
+
/**
|
|
417
|
+
* Anthropic Messages (`/v1/messages`). With `loop: { pipeline }` the
|
|
418
|
+
* exchange is recorded into those pipelines and the result carries
|
|
419
|
+
* `loop_sample_id` (see {@link LoopCaptureOption}). For serverless models:
|
|
420
|
+
* a dedicated deployment, a pipeline's `loop-<tag>` model included, serves
|
|
421
|
+
* the OpenAI shape and refuses this with 400; call it with `chatCompletions`.
|
|
422
|
+
*/
|
|
423
|
+
messages(params: AnthropicMessageParams): Promise<AnthropicMessageResponse>;
|
|
363
424
|
streamCompletions(params: CompletionParams): AsyncGenerator<Record<string, unknown>>;
|
|
425
|
+
/** Anthropic Messages, streamed. With `loop`, `loop.onSampleId` hears the sample id before the first event. */
|
|
364
426
|
streamMessages(params: AnthropicMessageParams): AsyncGenerator<Record<string, unknown>>;
|
|
365
427
|
listModels(inferenceKey?: string): Promise<InferenceModelListResponse>;
|
|
366
428
|
retrieveModel(modelId: string, inferenceKey?: string): Promise<InferenceModel>;
|
|
429
|
+
/**
|
|
430
|
+
* OpenAI chat completions (`/v1/chat/completions`). With
|
|
431
|
+
* `loop: { pipeline: 'support-replies' }` the exchange is recorded into
|
|
432
|
+
* that pipeline and the result carries `loop_sample_id`, the id to send
|
|
433
|
+
* feedback on (see {@link LoopCaptureOption}).
|
|
434
|
+
*/
|
|
367
435
|
chatCompletions(params: ChatCompletionParams): Promise<ChatCompletionResponse>;
|
|
436
|
+
/**
|
|
437
|
+
* OpenAI chat completions, streamed. With `loop`, `loop.onSampleId` hears
|
|
438
|
+
* the sample id as soon as the headers arrive, before the first chunk.
|
|
439
|
+
*/
|
|
368
440
|
streamChatCompletions(params: ChatCompletionParams): AsyncGenerator<ChatCompletionChunk>;
|
|
369
441
|
}
|
|
370
442
|
export { buildInferenceRequest };
|
|
@@ -135,6 +135,43 @@ function buildInferenceRequest(params) {
|
|
|
135
135
|
body.max_price_hour_cents = params.maxPriceHourCents;
|
|
136
136
|
return body;
|
|
137
137
|
}
|
|
138
|
+
/** The request header the `loop` option sends. */
|
|
139
|
+
export const LOOP_PIPELINE_HEADER = 'X-Loop-Pipeline';
|
|
140
|
+
/** The response header the gateway answers a recorded exchange with: the sample's id. */
|
|
141
|
+
export const LOOP_SAMPLE_ID_HEADER = 'X-Loop-Sample-Id';
|
|
142
|
+
/** `X-Loop-Pipeline`'s value for a `loop` option, or a thrown Error before any request. */
|
|
143
|
+
function loopPipelineHeader(loop) {
|
|
144
|
+
if (!loop || typeof loop !== 'object')
|
|
145
|
+
throw new Error('loop must be an object: { pipeline }');
|
|
146
|
+
const tags = Array.isArray(loop.pipeline) ? loop.pipeline : [loop.pipeline];
|
|
147
|
+
if (tags.length === 0)
|
|
148
|
+
throw new Error('loop.pipeline must name at least one pipeline tag');
|
|
149
|
+
return tags.map((tag) => {
|
|
150
|
+
const value = typeof tag === 'string' ? tag.trim() : '';
|
|
151
|
+
// A comma would split one tag into two, and a control character would end
|
|
152
|
+
// the header: both are refused here rather than sent.
|
|
153
|
+
if (!value || /[,\u0000-\u001f\u007f]/.test(value)) {
|
|
154
|
+
throw new Error(`loop.pipeline: ${JSON.stringify(tag)} is not a pipeline tag`);
|
|
155
|
+
}
|
|
156
|
+
return value;
|
|
157
|
+
}).join(', ');
|
|
158
|
+
}
|
|
159
|
+
/** The sample id a response carries (null when none), told to the caller's onSampleId once. */
|
|
160
|
+
function takeLoopSampleId(response, loop) {
|
|
161
|
+
const id = response.headers.get(LOOP_SAMPLE_ID_HEADER)?.trim() || null;
|
|
162
|
+
if (loop?.onSampleId)
|
|
163
|
+
loop.onSampleId(id);
|
|
164
|
+
return id;
|
|
165
|
+
}
|
|
166
|
+
/** `loop_sample_id` on a non-streaming result, non-enumerable. */
|
|
167
|
+
function withLoopSampleId(payload, id) {
|
|
168
|
+
if (payload && typeof payload === 'object' && !Array.isArray(payload)) {
|
|
169
|
+
Object.defineProperty(payload, 'loop_sample_id', {
|
|
170
|
+
value: id, enumerable: false, writable: false, configurable: true,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return payload;
|
|
174
|
+
}
|
|
138
175
|
function serverlessRead(value, valid) {
|
|
139
176
|
const body = value && typeof value === 'object' && !Array.isArray(value) ? value : null;
|
|
140
177
|
if (!body || !valid(body)) {
|
|
@@ -336,7 +373,7 @@ export class Inference {
|
|
|
336
373
|
this.key = config.inferenceKey || envInferenceKey() || envApiKey();
|
|
337
374
|
// Same default host as HttpClient (api.runbios.ai); api.runbios.ai cutover
|
|
338
375
|
// is planned once its DNS exists — update both call sites together.
|
|
339
|
-
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api.runbios.ai').replace(/\/+$/, '');
|
|
376
|
+
this.baseUrl = (config.baseUrl || envBaseUrl() || 'https://api-dev.runbios.ai').replace(/\/+$/, '');
|
|
340
377
|
this.timeout = config.timeout ?? 900_000;
|
|
341
378
|
this._http = http;
|
|
342
379
|
}
|
|
@@ -792,7 +829,7 @@ export class Inference {
|
|
|
792
829
|
// OpenAI-compatible key-scoped inference — `/v1/chat/completions`
|
|
793
830
|
// --------------------------------------------------------------------------
|
|
794
831
|
prepare(params, stream) {
|
|
795
|
-
const { messages, model, tools, toolChoice, reasoningEffort, inferenceKey, idempotencyKey, requestId, signal, ...extra } = params;
|
|
832
|
+
const { messages, model, tools, toolChoice, reasoningEffort, inferenceKey, idempotencyKey, requestId, signal, loop, ...extra } = params;
|
|
796
833
|
const key = inferenceKey || this.key;
|
|
797
834
|
if (!key)
|
|
798
835
|
throw new Error('an inferenceKey is required');
|
|
@@ -814,6 +851,8 @@ export class Inference {
|
|
|
814
851
|
};
|
|
815
852
|
if (idempotencyKey)
|
|
816
853
|
headers['Idempotency-Key'] = idempotencyKey;
|
|
854
|
+
if (loop !== undefined)
|
|
855
|
+
headers[LOOP_PIPELINE_HEADER] = loopPipelineHeader(loop);
|
|
817
856
|
return { body, key, headers, signal };
|
|
818
857
|
}
|
|
819
858
|
abortContext(signal) {
|
|
@@ -831,7 +870,10 @@ export class Inference {
|
|
|
831
870
|
};
|
|
832
871
|
}
|
|
833
872
|
prepareVerb(params, stream, anthropic) {
|
|
834
|
-
const { inferenceKey, idempotencyKey, requestId, signal, anthropicVersion, stream: requestedStream, ...payload } = params;
|
|
873
|
+
const { inferenceKey, idempotencyKey, requestId, signal, anthropicVersion, stream: requestedStream, loop, ...payload } = params;
|
|
874
|
+
if (loop !== undefined && !anthropic) {
|
|
875
|
+
throw new Error('loop capture is offered on chatCompletions and messages only');
|
|
876
|
+
}
|
|
835
877
|
if (requestedStream !== undefined)
|
|
836
878
|
throw new Error('Choose the streaming method instead of setting stream in a request body');
|
|
837
879
|
const key = inferenceKey || this.key;
|
|
@@ -851,6 +893,8 @@ export class Inference {
|
|
|
851
893
|
}
|
|
852
894
|
headers['Anthropic-Version'] = anthropicVersion || '2023-06-01';
|
|
853
895
|
}
|
|
896
|
+
if (loop !== undefined)
|
|
897
|
+
headers[LOOP_PIPELINE_HEADER] = loopPipelineHeader(loop);
|
|
854
898
|
return { body: stream ? { ...payload, stream: true } : payload, headers, signal };
|
|
855
899
|
}
|
|
856
900
|
async sendVerb(path, params, anthropic = false) {
|
|
@@ -862,6 +906,8 @@ export class Inference {
|
|
|
862
906
|
});
|
|
863
907
|
if (!response.ok)
|
|
864
908
|
throw await apiError(response);
|
|
909
|
+
const loop = anthropic ? params.loop : undefined;
|
|
910
|
+
const sampleId = anthropic ? takeLoopSampleId(response, loop) : null;
|
|
865
911
|
let payload;
|
|
866
912
|
try {
|
|
867
913
|
payload = await response.json();
|
|
@@ -872,7 +918,7 @@ export class Inference {
|
|
|
872
918
|
if (!payload || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
873
919
|
throw new ApiError(response.status, { error: 'Inference endpoint returned no response object' });
|
|
874
920
|
}
|
|
875
|
-
return payload;
|
|
921
|
+
return anthropic ? withLoopSampleId(payload, sampleId) : payload;
|
|
876
922
|
}
|
|
877
923
|
catch (error) {
|
|
878
924
|
if (abort.controller.signal.aborted && !(error instanceof ApiError)) {
|
|
@@ -929,6 +975,8 @@ export class Inference {
|
|
|
929
975
|
if (!contentType.includes('text/event-stream')) {
|
|
930
976
|
throw new ApiError(response.status, { error: `Expected text/event-stream, received ${contentType || 'no content type'}` });
|
|
931
977
|
}
|
|
978
|
+
if (anthropic)
|
|
979
|
+
takeLoopSampleId(response, params.loop);
|
|
932
980
|
if (!response.body)
|
|
933
981
|
throw new ApiError(0, { error: 'Inference stream returned no body' });
|
|
934
982
|
for await (const data of parseSSE(response.body)) {
|
|
@@ -976,12 +1024,20 @@ export class Inference {
|
|
|
976
1024
|
rerank(params) {
|
|
977
1025
|
return this.sendVerb('/v1/rerank', params);
|
|
978
1026
|
}
|
|
1027
|
+
/**
|
|
1028
|
+
* Anthropic Messages (`/v1/messages`). With `loop: { pipeline }` the
|
|
1029
|
+
* exchange is recorded into those pipelines and the result carries
|
|
1030
|
+
* `loop_sample_id` (see {@link LoopCaptureOption}). For serverless models:
|
|
1031
|
+
* a dedicated deployment, a pipeline's `loop-<tag>` model included, serves
|
|
1032
|
+
* the OpenAI shape and refuses this with 400; call it with `chatCompletions`.
|
|
1033
|
+
*/
|
|
979
1034
|
messages(params) {
|
|
980
1035
|
return this.sendVerb('/v1/messages', params, true);
|
|
981
1036
|
}
|
|
982
1037
|
async *streamCompletions(params) {
|
|
983
1038
|
yield* this.streamVerb('/v1/completions', params);
|
|
984
1039
|
}
|
|
1040
|
+
/** Anthropic Messages, streamed. With `loop`, `loop.onSampleId` hears the sample id before the first event. */
|
|
985
1041
|
async *streamMessages(params) {
|
|
986
1042
|
yield* this.streamVerb('/v1/messages', params, true);
|
|
987
1043
|
}
|
|
@@ -994,6 +1050,12 @@ export class Inference {
|
|
|
994
1050
|
throw new Error('RunBiOS: modelId is required to retrieve an inference model');
|
|
995
1051
|
return asInferenceModel(await this.modelRead(`/v1/models/${encodeURIComponent(id)}`, inferenceKey));
|
|
996
1052
|
}
|
|
1053
|
+
/**
|
|
1054
|
+
* OpenAI chat completions (`/v1/chat/completions`). With
|
|
1055
|
+
* `loop: { pipeline: 'support-replies' }` the exchange is recorded into
|
|
1056
|
+
* that pipeline and the result carries `loop_sample_id`, the id to send
|
|
1057
|
+
* feedback on (see {@link LoopCaptureOption}).
|
|
1058
|
+
*/
|
|
997
1059
|
async chatCompletions(params) {
|
|
998
1060
|
const prepared = this.prepare(params, false);
|
|
999
1061
|
const abort = this.abortContext(prepared.signal);
|
|
@@ -1003,7 +1065,8 @@ export class Inference {
|
|
|
1003
1065
|
});
|
|
1004
1066
|
if (!response.ok)
|
|
1005
1067
|
throw await apiError(response);
|
|
1006
|
-
|
|
1068
|
+
const sampleId = takeLoopSampleId(response, params.loop);
|
|
1069
|
+
return withLoopSampleId(await response.json(), sampleId);
|
|
1007
1070
|
}
|
|
1008
1071
|
catch (error) {
|
|
1009
1072
|
if (abort.controller.signal.aborted && !(error instanceof ApiError)) {
|
|
@@ -1016,6 +1079,10 @@ export class Inference {
|
|
|
1016
1079
|
abort.remove();
|
|
1017
1080
|
}
|
|
1018
1081
|
}
|
|
1082
|
+
/**
|
|
1083
|
+
* OpenAI chat completions, streamed. With `loop`, `loop.onSampleId` hears
|
|
1084
|
+
* the sample id as soon as the headers arrive, before the first chunk.
|
|
1085
|
+
*/
|
|
1019
1086
|
async *streamChatCompletions(params) {
|
|
1020
1087
|
const prepared = this.prepare(params, true);
|
|
1021
1088
|
const abort = this.abortContext(prepared.signal);
|
|
@@ -1029,6 +1096,7 @@ export class Inference {
|
|
|
1029
1096
|
if (!contentType.includes('text/event-stream')) {
|
|
1030
1097
|
throw new ApiError(response.status, { error: `Expected text/event-stream, received ${contentType || 'no content type'}` });
|
|
1031
1098
|
}
|
|
1099
|
+
takeLoopSampleId(response, params.loop);
|
|
1032
1100
|
if (!response.body)
|
|
1033
1101
|
throw new ApiError(0, { error: 'Inference response has no body' });
|
|
1034
1102
|
for await (const data of parseSSE(response.body)) {
|