@proposit/proposit-core 2.1.0 → 2.2.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/dist/extensions/chat-completions/errors.d.ts +66 -0
- package/dist/extensions/chat-completions/errors.d.ts.map +1 -0
- package/dist/extensions/chat-completions/errors.js +139 -0
- package/dist/extensions/chat-completions/errors.js.map +1 -0
- package/dist/extensions/chat-completions/http.d.ts +10 -0
- package/dist/extensions/chat-completions/http.d.ts.map +1 -0
- package/dist/extensions/chat-completions/http.js +80 -0
- package/dist/extensions/chat-completions/http.js.map +1 -0
- package/dist/extensions/chat-completions/index.d.ts +7 -0
- package/dist/extensions/chat-completions/index.d.ts.map +1 -0
- package/dist/extensions/chat-completions/index.js +18 -0
- package/dist/extensions/chat-completions/index.js.map +1 -0
- package/dist/extensions/chat-completions/provider.d.ts +5 -0
- package/dist/extensions/chat-completions/provider.d.ts.map +1 -0
- package/dist/extensions/chat-completions/provider.js +192 -0
- package/dist/extensions/chat-completions/provider.js.map +1 -0
- package/dist/extensions/chat-completions/structured-output.d.ts +18 -0
- package/dist/extensions/chat-completions/structured-output.d.ts.map +1 -0
- package/dist/extensions/{ollama → chat-completions}/structured-output.js +14 -10
- package/dist/extensions/chat-completions/structured-output.js.map +1 -0
- package/dist/extensions/chat-completions/types.d.ts +65 -0
- package/dist/extensions/chat-completions/types.d.ts.map +1 -0
- package/dist/extensions/chat-completions/types.js +19 -0
- package/dist/extensions/chat-completions/types.js.map +1 -0
- package/dist/extensions/openai/provider.d.ts +2 -2
- package/dist/extensions/pipelines/base/types.d.ts +6 -5
- package/dist/extensions/pipelines/base/types.d.ts.map +1 -1
- package/dist/lib/pipelines/stage-helpers.d.ts.map +1 -1
- package/dist/lib/pipelines/stage-helpers.js +37 -1
- package/dist/lib/pipelines/stage-helpers.js.map +1 -1
- package/dist/lib/pipelines/types.d.ts +2 -2
- package/package.json +6 -16
- package/dist/extensions/ollama/errors.d.ts +0 -73
- package/dist/extensions/ollama/errors.d.ts.map +0 -1
- package/dist/extensions/ollama/errors.js +0 -228
- package/dist/extensions/ollama/errors.js.map +0 -1
- package/dist/extensions/ollama/index.d.ts +0 -6
- package/dist/extensions/ollama/index.d.ts.map +0 -1
- package/dist/extensions/ollama/index.js +0 -17
- package/dist/extensions/ollama/index.js.map +0 -1
- package/dist/extensions/ollama/provider.d.ts +0 -22
- package/dist/extensions/ollama/provider.d.ts.map +0 -1
- package/dist/extensions/ollama/provider.js +0 -417
- package/dist/extensions/ollama/provider.js.map +0 -1
- package/dist/extensions/ollama/structured-output.d.ts +0 -18
- package/dist/extensions/ollama/structured-output.d.ts.map +0 -1
- package/dist/extensions/ollama/structured-output.js.map +0 -1
- package/dist/extensions/ollama/timeout-fetch.d.ts +0 -24
- package/dist/extensions/ollama/timeout-fetch.d.ts.map +0 -1
- package/dist/extensions/ollama/timeout-fetch.js +0 -76
- package/dist/extensions/ollama/timeout-fetch.js.map +0 -1
- package/dist/extensions/ollama/types.d.ts +0 -219
- package/dist/extensions/ollama/types.d.ts.map +0 -1
- package/dist/extensions/ollama/types.js +0 -7
- package/dist/extensions/ollama/types.js.map +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export declare class TransientLlmError extends Error {
|
|
2
|
+
readonly retryReason: "transient";
|
|
3
|
+
readonly status?: number;
|
|
4
|
+
constructor(args: {
|
|
5
|
+
message: string;
|
|
6
|
+
status?: number;
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
export declare class RateLimitLlmError extends Error {
|
|
10
|
+
readonly retryReason: "rate_limit";
|
|
11
|
+
readonly status?: number;
|
|
12
|
+
constructor(args: {
|
|
13
|
+
message: string;
|
|
14
|
+
status?: number;
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Persistent provider budget exhaustion — surfaced as a 429 whose body
|
|
19
|
+
* carries an `insufficient_quota` code/type. Distinct from the transient
|
|
20
|
+
* {@link RateLimitLlmError} throttle: the framework reads the
|
|
21
|
+
* `quota_exhausted` tag, which is absent from every default `retryOn`,
|
|
22
|
+
* so the stage fails fast on attempt 1.
|
|
23
|
+
*/
|
|
24
|
+
export declare class QuotaExhaustedLlmError extends Error {
|
|
25
|
+
readonly retryReason: "quota_exhausted";
|
|
26
|
+
readonly status?: number;
|
|
27
|
+
constructor(args: {
|
|
28
|
+
message: string;
|
|
29
|
+
status?: number;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Thrown when the endpoint rejects our request because the model's
|
|
34
|
+
* output failed server-side JSON-Schema enforcement (typical 422).
|
|
35
|
+
* Tagged `transient` so the framework's default retry policy retries — a
|
|
36
|
+
* single re-roll often produces conforming output.
|
|
37
|
+
*/
|
|
38
|
+
export declare class SchemaValidationLlmError extends Error {
|
|
39
|
+
readonly retryReason: "transient";
|
|
40
|
+
readonly status?: number;
|
|
41
|
+
constructor(args: {
|
|
42
|
+
message: string;
|
|
43
|
+
status?: number;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export declare class NonRetryableLlmError extends Error {
|
|
47
|
+
readonly status?: number;
|
|
48
|
+
constructor(args: {
|
|
49
|
+
message: string;
|
|
50
|
+
status?: number;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Route an HTTP status family (plus an optional structured provider
|
|
55
|
+
* error code) into the framework-recognized error class.
|
|
56
|
+
*/
|
|
57
|
+
export declare function classifyHttpError(status: number, message: string, providerErrorCode?: string): Error;
|
|
58
|
+
/**
|
|
59
|
+
* Map a low-level `fetch` failure (server unreachable, connection reset,
|
|
60
|
+
* the request timeout firing as a non-Abort transport error) to a
|
|
61
|
+
* transient error. Talking to a local OpenAI-compatible server that is
|
|
62
|
+
* down or mid-generation is retryable — a retry after backoff can
|
|
63
|
+
* succeed once the server is up / a slow generation completes.
|
|
64
|
+
*/
|
|
65
|
+
export declare function classifyFetchError(err: unknown): Error;
|
|
66
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/extensions/chat-completions/errors.ts"],"names":[],"mappings":"AAmCA,qBAAa,iBAAkB,SAAQ,KAAK;IACxC,SAAgB,WAAW,EAAG,WAAW,CAAS;IAClD,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAA;gBAEnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAKzD;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IACxC,SAAgB,WAAW,EAAG,YAAY,CAAS;IACnD,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAA;gBAEnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAKzD;AAED;;;;;;GAMG;AACH,qBAAa,sBAAuB,SAAQ,KAAK;IAC7C,SAAgB,WAAW,EAAG,iBAAiB,CAAS;IACxD,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAA;gBAEnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAKzD;AAED;;;;;GAKG;AACH,qBAAa,wBAAyB,SAAQ,KAAK;IAC/C,SAAgB,WAAW,EAAG,WAAW,CAAS;IAClD,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAA;gBAEnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAKzD;AAED,qBAAa,oBAAqB,SAAQ,KAAK;IAC3C,SAAgB,MAAM,CAAC,EAAE,MAAM,CAAA;gBAEnB,IAAI,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE;CAKzD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC7B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,iBAAiB,CAAC,EAAE,MAAM,GAC3B,KAAK,CA6BP;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,KAAK,CAKtD"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// chat-completions-provider error classes + the `classifyHttpError` /
|
|
2
|
+
// `classifyFetchError` mappings.
|
|
3
|
+
//
|
|
4
|
+
// The framework's `llmStage` retry policy classifies provider errors by
|
|
5
|
+
// inspecting a `retryReason` tag on the thrown object (see
|
|
6
|
+
// `src/lib/pipelines/stage-helpers.ts#classifyError`). To play cleanly
|
|
7
|
+
// with that mechanism, each error class carries the appropriate tag as
|
|
8
|
+
// an own property. The framework derives the `TProcessingFailure.code`
|
|
9
|
+
// from the tag, so these classes need no `code` of their own.
|
|
10
|
+
//
|
|
11
|
+
// The class names intentionally mirror the OpenAI provider's names but
|
|
12
|
+
// are *distinct* classes living in this extension; they are surfaced
|
|
13
|
+
// only from this subpath (NOT the package root) to avoid colliding with
|
|
14
|
+
// the root-exported OpenAI error classes.
|
|
15
|
+
//
|
|
16
|
+
// Mapping for the framework's default retry policy
|
|
17
|
+
// (`retryOn: ["schema_validation", "transient"]`):
|
|
18
|
+
//
|
|
19
|
+
// * `TransientLlmError` — `retryReason: "transient"`. 5xx responses +
|
|
20
|
+
// low-level fetch failures (server unreachable, connection reset, a
|
|
21
|
+
// request timeout). Retried under the default policy.
|
|
22
|
+
// * `RateLimitLlmError` — `retryReason: "rate_limit"`. A 429 whose body
|
|
23
|
+
// is NOT `insufficient_quota`. Not retried by default — callers can
|
|
24
|
+
// opt into `retryOn: ["..., "rate_limit"]`.
|
|
25
|
+
// * `QuotaExhaustedLlmError` — `retryReason: "quota_exhausted"`.
|
|
26
|
+
// Persistent budget exhaustion (a 429 whose body code/type is
|
|
27
|
+
// `insufficient_quota`). Fail-fast: the tag is absent from every
|
|
28
|
+
// default `retryOn`, so the stage breaks on attempt 1.
|
|
29
|
+
// * `SchemaValidationLlmError` — `retryReason: "transient"`. The
|
|
30
|
+
// model's output failed schema enforcement on the server side
|
|
31
|
+
// (typical 422). A single re-roll often produces conforming output.
|
|
32
|
+
// * `NonRetryableLlmError` — no tag; the framework classifies it as
|
|
33
|
+
// `non_retryable` and surfaces it immediately. Used for 400/401/403
|
|
34
|
+
// and other unrecoverable 4xx.
|
|
35
|
+
export class TransientLlmError extends Error {
|
|
36
|
+
retryReason = "transient";
|
|
37
|
+
status;
|
|
38
|
+
constructor(args) {
|
|
39
|
+
super(args.message);
|
|
40
|
+
this.name = "TransientLlmError";
|
|
41
|
+
this.status = args.status;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class RateLimitLlmError extends Error {
|
|
45
|
+
retryReason = "rate_limit";
|
|
46
|
+
status;
|
|
47
|
+
constructor(args) {
|
|
48
|
+
super(args.message);
|
|
49
|
+
this.name = "RateLimitLlmError";
|
|
50
|
+
this.status = args.status;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Persistent provider budget exhaustion — surfaced as a 429 whose body
|
|
55
|
+
* carries an `insufficient_quota` code/type. Distinct from the transient
|
|
56
|
+
* {@link RateLimitLlmError} throttle: the framework reads the
|
|
57
|
+
* `quota_exhausted` tag, which is absent from every default `retryOn`,
|
|
58
|
+
* so the stage fails fast on attempt 1.
|
|
59
|
+
*/
|
|
60
|
+
export class QuotaExhaustedLlmError extends Error {
|
|
61
|
+
retryReason = "quota_exhausted";
|
|
62
|
+
status;
|
|
63
|
+
constructor(args) {
|
|
64
|
+
super(args.message);
|
|
65
|
+
this.name = "QuotaExhaustedLlmError";
|
|
66
|
+
this.status = args.status;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Thrown when the endpoint rejects our request because the model's
|
|
71
|
+
* output failed server-side JSON-Schema enforcement (typical 422).
|
|
72
|
+
* Tagged `transient` so the framework's default retry policy retries — a
|
|
73
|
+
* single re-roll often produces conforming output.
|
|
74
|
+
*/
|
|
75
|
+
export class SchemaValidationLlmError extends Error {
|
|
76
|
+
retryReason = "transient";
|
|
77
|
+
status;
|
|
78
|
+
constructor(args) {
|
|
79
|
+
super(args.message);
|
|
80
|
+
this.name = "SchemaValidationLlmError";
|
|
81
|
+
this.status = args.status;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export class NonRetryableLlmError extends Error {
|
|
85
|
+
status;
|
|
86
|
+
constructor(args) {
|
|
87
|
+
super(args.message);
|
|
88
|
+
this.name = "NonRetryableLlmError";
|
|
89
|
+
this.status = args.status;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Route an HTTP status family (plus an optional structured provider
|
|
94
|
+
* error code) into the framework-recognized error class.
|
|
95
|
+
*/
|
|
96
|
+
export function classifyHttpError(status, message, providerErrorCode) {
|
|
97
|
+
if (status >= 500) {
|
|
98
|
+
return new TransientLlmError({ message, status });
|
|
99
|
+
}
|
|
100
|
+
if (status === 429) {
|
|
101
|
+
// 429 splits on the body's structured error code: persistent
|
|
102
|
+
// budget exhaustion (`insufficient_quota`) is fail-fast, every
|
|
103
|
+
// other (and every unparseable) 429 stays the transient throttle.
|
|
104
|
+
// The safe default is always "transient + retryable" — never a
|
|
105
|
+
// false quota trip.
|
|
106
|
+
if (providerErrorCode === "insufficient_quota") {
|
|
107
|
+
return new QuotaExhaustedLlmError({ message, status });
|
|
108
|
+
}
|
|
109
|
+
return new RateLimitLlmError({ message, status });
|
|
110
|
+
}
|
|
111
|
+
// 400 is a malformed request — a converter bug, an unsupported
|
|
112
|
+
// parameter, or a request shape the endpoint doesn't accept;
|
|
113
|
+
// retrying just burns the second attempt, so classify non-retryable.
|
|
114
|
+
if (status === 400) {
|
|
115
|
+
return new NonRetryableLlmError({ message, status });
|
|
116
|
+
}
|
|
117
|
+
// 422 signals the model's structured-output reply failed server-side
|
|
118
|
+
// schema validation; a re-roll can succeed, so route it through the
|
|
119
|
+
// schema-validation class (tagged transient).
|
|
120
|
+
if (status === 422) {
|
|
121
|
+
return new SchemaValidationLlmError({ message, status });
|
|
122
|
+
}
|
|
123
|
+
// 401/403 and every other 4xx are unrecoverable.
|
|
124
|
+
return new NonRetryableLlmError({ message, status });
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Map a low-level `fetch` failure (server unreachable, connection reset,
|
|
128
|
+
* the request timeout firing as a non-Abort transport error) to a
|
|
129
|
+
* transient error. Talking to a local OpenAI-compatible server that is
|
|
130
|
+
* down or mid-generation is retryable — a retry after backoff can
|
|
131
|
+
* succeed once the server is up / a slow generation completes.
|
|
132
|
+
*/
|
|
133
|
+
export function classifyFetchError(err) {
|
|
134
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
135
|
+
return new TransientLlmError({
|
|
136
|
+
message: `Network error calling the chat-completions endpoint: ${message}`,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../src/extensions/chat-completions/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TChatCompletionsFetch, TChatCompletionsResponse } from "./types.js";
|
|
2
|
+
export declare function requestJson(args: {
|
|
3
|
+
url: string;
|
|
4
|
+
apiKey: string;
|
|
5
|
+
body: unknown;
|
|
6
|
+
fetchImpl: TChatCompletionsFetch;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
timeoutMs: number;
|
|
9
|
+
}): Promise<TChatCompletionsResponse>;
|
|
10
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../../src/extensions/chat-completions/http.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACR,qBAAqB,EACrB,wBAAwB,EAC3B,MAAM,YAAY,CAAA;AA2BnB,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACpC,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,OAAO,CAAA;IACb,SAAS,EAAE,qBAAqB,CAAA;IAChC,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CACpB,GAAG,OAAO,CAAC,wBAAwB,CAAC,CA2DpC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Raw-fetch HTTP transport for an OpenAI-compatible
|
|
2
|
+
// `/v1/chat/completions` endpoint: a single POST of JSON, a per-request
|
|
3
|
+
// timeout enforced via `AbortSignal.timeout` (composed with the caller's
|
|
4
|
+
// abort signal so a mid-flight cancel still propagates), and non-2xx →
|
|
5
|
+
// error-class classification. No SSE, no background poll — this provider
|
|
6
|
+
// is synchronous. The timeout is a standard `AbortSignal.timeout` with
|
|
7
|
+
// no extra HTTP-stack dependency.
|
|
8
|
+
import { classifyFetchError, classifyHttpError, TransientLlmError, } from "./errors.js";
|
|
9
|
+
function isAbortError(err) {
|
|
10
|
+
return (typeof err === "object" &&
|
|
11
|
+
err !== null &&
|
|
12
|
+
err.name === "AbortError");
|
|
13
|
+
}
|
|
14
|
+
// Compose the caller's AbortSignal with a timeout signal so EITHER one
|
|
15
|
+
// aborts the request. `AbortSignal.any` / `AbortSignal.timeout` are
|
|
16
|
+
// available on the supported Node floor (>=22). When no timeout is
|
|
17
|
+
// configured (timeoutMs <= 0) and no caller signal is present, returns
|
|
18
|
+
// undefined (no abort).
|
|
19
|
+
function resolveSignal(callerSignal, timeoutMs) {
|
|
20
|
+
const timeoutSignal = timeoutMs > 0 ? AbortSignal.timeout(timeoutMs) : undefined;
|
|
21
|
+
if (callerSignal && timeoutSignal) {
|
|
22
|
+
return AbortSignal.any([callerSignal, timeoutSignal]);
|
|
23
|
+
}
|
|
24
|
+
return callerSignal ?? timeoutSignal;
|
|
25
|
+
}
|
|
26
|
+
export async function requestJson(args) {
|
|
27
|
+
const signal = resolveSignal(args.signal, args.timeoutMs);
|
|
28
|
+
let response;
|
|
29
|
+
try {
|
|
30
|
+
response = await args.fetchImpl(args.url, {
|
|
31
|
+
method: "POST",
|
|
32
|
+
headers: {
|
|
33
|
+
"Content-Type": "application/json",
|
|
34
|
+
Authorization: `Bearer ${args.apiKey}`,
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify(args.body),
|
|
37
|
+
signal,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
// A caller-initiated abort propagates verbatim so `llmStage`'s
|
|
42
|
+
// mid-flight-abort detector marks the stage skipped — gated on the
|
|
43
|
+
// caller's signal actually having fired (a caller abort surfaces as
|
|
44
|
+
// an `AbortError`). A timeout surfaces as a `TimeoutError` (not an
|
|
45
|
+
// `AbortError`), and every other transport failure surfaces with
|
|
46
|
+
// its own name — so both fall through to `classifyFetchError` and
|
|
47
|
+
// are retried as transient.
|
|
48
|
+
if (isAbortError(err) && args.signal?.aborted) {
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
|
+
throw classifyFetchError(err);
|
|
52
|
+
}
|
|
53
|
+
if (!response.ok) {
|
|
54
|
+
const errorBody = await response.text().catch(() => "");
|
|
55
|
+
const message = `Chat-completions endpoint ${response.status.toString()}: ${errorBody || response.statusText}`;
|
|
56
|
+
// Best-effort structured extraction of the provider error
|
|
57
|
+
// code/type so a 429 can be split into persistent quota
|
|
58
|
+
// exhaustion vs. transient throttling. An unparseable body leaves
|
|
59
|
+
// `providerErrorCode` undefined, which `classifyHttpError` treats
|
|
60
|
+
// as the safe transient default.
|
|
61
|
+
let providerErrorCode;
|
|
62
|
+
try {
|
|
63
|
+
const parsed = JSON.parse(errorBody);
|
|
64
|
+
providerErrorCode = parsed.error?.code ?? parsed.error?.type;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
providerErrorCode = undefined;
|
|
68
|
+
}
|
|
69
|
+
throw classifyHttpError(response.status, message, providerErrorCode);
|
|
70
|
+
}
|
|
71
|
+
return response
|
|
72
|
+
.json()
|
|
73
|
+
.then((j) => j)
|
|
74
|
+
.catch((err) => {
|
|
75
|
+
throw new TransientLlmError({
|
|
76
|
+
message: `Chat-completions response body was not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../../src/extensions/chat-completions/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { createChatCompletionsProvider } from "./provider.js";
|
|
2
|
+
export type { TCreateChatCompletionsProviderOptions } from "./provider.js";
|
|
3
|
+
export type { TChatCompletionsProviderConfig } from "./types.js";
|
|
4
|
+
export { typeboxToJsonSchema } from "./structured-output.js";
|
|
5
|
+
export type { TChatCompletionsJsonSchema } from "./structured-output.js";
|
|
6
|
+
export { NonRetryableLlmError, QuotaExhaustedLlmError, RateLimitLlmError, SchemaValidationLlmError, TransientLlmError, classifyHttpError, classifyFetchError, } from "./errors.js";
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/chat-completions/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAC7D,YAAY,EAAE,qCAAqC,EAAE,MAAM,eAAe,CAAA;AAC1E,YAAY,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAC5D,YAAY,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EACH,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACrB,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Barrel for the chat-completions provider extension.
|
|
2
|
+
//
|
|
3
|
+
// Public surface consumed via the
|
|
4
|
+
// `@proposit/proposit-core/extensions/chat-completions` subpath: the
|
|
5
|
+
// provider factory + its config type + the lax JSON-schema converter +
|
|
6
|
+
// the error classes (which callers may `instanceof`-match for finer-
|
|
7
|
+
// grained observability) and the status/fetch classifiers.
|
|
8
|
+
//
|
|
9
|
+
// The error class names intentionally mirror the OpenAI provider's names
|
|
10
|
+
// but are *distinct* classes living in this extension; they are surfaced
|
|
11
|
+
// only from this subpath (NOT the package root) to avoid colliding with
|
|
12
|
+
// the root-exported OpenAI error classes. The framework classifies by
|
|
13
|
+
// the `retryReason` tag, not class identity, so the duplication is
|
|
14
|
+
// intentional and harmless.
|
|
15
|
+
export { createChatCompletionsProvider } from "./provider.js";
|
|
16
|
+
export { typeboxToJsonSchema } from "./structured-output.js";
|
|
17
|
+
export { NonRetryableLlmError, QuotaExhaustedLlmError, RateLimitLlmError, SchemaValidationLlmError, TransientLlmError, classifyHttpError, classifyFetchError, } from "./errors.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/chat-completions/index.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,EAAE;AACF,kCAAkC;AAClC,qEAAqE;AACrE,uEAAuE;AACvE,qEAAqE;AACrE,2DAA2D;AAC3D,EAAE;AACF,yEAAyE;AACzE,yEAAyE;AACzE,wEAAwE;AACxE,sEAAsE;AACtE,mEAAmE;AACnE,4BAA4B;AAE5B,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAG7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;AAE5D,OAAO,EACH,oBAAoB,EACpB,sBAAsB,EACtB,iBAAiB,EACjB,wBAAwB,EACxB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,GACrB,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TLlmProvider } from "../../lib/llm/types.js";
|
|
2
|
+
import { type TChatCompletionsProviderConfig } from "./types.js";
|
|
3
|
+
export type TCreateChatCompletionsProviderOptions = TChatCompletionsProviderConfig;
|
|
4
|
+
export declare function createChatCompletionsProvider(options?: TChatCompletionsProviderConfig): TLlmProvider;
|
|
5
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../src/extensions/chat-completions/provider.ts"],"names":[],"mappings":"AA4BA,OAAO,KAAK,EACR,YAAY,EAIf,MAAM,wBAAwB,CAAA;AAS/B,OAAO,EAMH,KAAK,8BAA8B,EAEtC,MAAM,YAAY,CAAA;AAInB,MAAM,MAAM,qCAAqC,GAC7C,8BAA8B,CAAA;AAoBlC,wBAAgB,6BAA6B,CACzC,OAAO,CAAC,EAAE,8BAA8B,GACzC,YAAY,CA8Jd"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
// Concrete `TLlmProvider` backed by an OpenAI-compatible
|
|
2
|
+
// `/v1/chat/completions` endpoint (a local llama-server by default, or
|
|
3
|
+
// any OpenAI-compatible chat backend by swapping baseUrl + token).
|
|
4
|
+
//
|
|
5
|
+
// Synchronous, structured-output-only:
|
|
6
|
+
// * One POST to `{baseUrl}/chat/completions` via raw `fetch` (the
|
|
7
|
+
// `./http.ts` `requestJson` helper) — no SSE, no background/poll, no
|
|
8
|
+
// mid-flight responseId. `TLlmProvider.respond` is the whole surface;
|
|
9
|
+
// the optional `onResponseCreated` hook is left uncalled (a
|
|
10
|
+
// synchronous provider never learns an id mid-flight), which is
|
|
11
|
+
// contract-legal — the same posture the prior local provider took.
|
|
12
|
+
// * Structured output via the lax `typeboxToJsonSchema` converter under
|
|
13
|
+
// a `response_format: { type: "json_schema", json_schema: { ... } }`.
|
|
14
|
+
// * `reasoningEffort` is ignored (no chat-completions analogue);
|
|
15
|
+
// `maxOutputTokens` maps to `max_tokens` (positive values only).
|
|
16
|
+
// * Function tools are NOT supported — a request carrying `tools` fails
|
|
17
|
+
// fast with `NonRetryableLlmError`. This provider serves the
|
|
18
|
+
// structured-output ingestion path, which uses no tools, so there is
|
|
19
|
+
// no multi-round tool loop to port.
|
|
20
|
+
// * `requestTimeoutMs` is enforced via `AbortSignal.timeout` inside
|
|
21
|
+
// `requestJson` — no extra HTTP-stack dependency.
|
|
22
|
+
//
|
|
23
|
+
// Error classification routes HTTP-status families + fetch failures into
|
|
24
|
+
// the framework-recognized error classes (see `./errors.ts`). The
|
|
25
|
+
// framework's `llmStage` retry policy classifies via the `retryReason`
|
|
26
|
+
// tag on the thrown error.
|
|
27
|
+
import { debugLlmFailure, debugLlmRequest, debugLlmResponse, } from "../../lib/pipelines/debug-log.js";
|
|
28
|
+
import { typeboxToJsonSchema } from "./structured-output.js";
|
|
29
|
+
import { requestJson } from "./http.js";
|
|
30
|
+
import { NonRetryableLlmError, SchemaValidationLlmError } from "./errors.js";
|
|
31
|
+
import { DEFAULT_API_KEY, DEFAULT_BASE_URL, DEFAULT_MODEL, DEFAULT_REQUEST_TIMEOUT_MS, } from "./types.js";
|
|
32
|
+
const STAGE_ID_MARKER = /<!--\s*stage-id:\s*([^\s>]+)\s*-->/;
|
|
33
|
+
function abortError() {
|
|
34
|
+
const e = new Error("The chat-completions request was aborted.");
|
|
35
|
+
e.name = "AbortError";
|
|
36
|
+
return e;
|
|
37
|
+
}
|
|
38
|
+
// Derive the `json_schema.name` from the schema's `$id` (sanitized to the
|
|
39
|
+
// `^[a-zA-Z0-9_-]{1,64}$` shape OpenAI-compatible servers accept), or a
|
|
40
|
+
// stable default when the schema is anonymous.
|
|
41
|
+
function deriveSchemaName(schema) {
|
|
42
|
+
const id = schema.$id;
|
|
43
|
+
if (typeof id === "string" && id.length > 0) {
|
|
44
|
+
const cleaned = id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64);
|
|
45
|
+
if (cleaned.length > 0)
|
|
46
|
+
return cleaned;
|
|
47
|
+
}
|
|
48
|
+
return "structured_output";
|
|
49
|
+
}
|
|
50
|
+
export function createChatCompletionsProvider(options) {
|
|
51
|
+
const config = options ?? {};
|
|
52
|
+
const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
53
|
+
const url = `${baseUrl}/chat/completions`;
|
|
54
|
+
const apiKey = config.apiKey ?? DEFAULT_API_KEY;
|
|
55
|
+
// The provider's configured model is a fallback: a per-request
|
|
56
|
+
// `req.model` (the framework's normal path) wins; the configured
|
|
57
|
+
// model (or the `local-coder` default) applies only when a request
|
|
58
|
+
// carries no model.
|
|
59
|
+
const configuredModel = config.model ?? DEFAULT_MODEL;
|
|
60
|
+
const timeoutMs = config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
61
|
+
const fetchImpl = config.fetch ?? globalThis.fetch;
|
|
62
|
+
if (!fetchImpl) {
|
|
63
|
+
throw new Error("createChatCompletionsProvider: no fetch implementation available. Pass `fetch` explicitly or run in an environment that provides `globalThis.fetch` (Node ≥18, modern browsers, Expo).");
|
|
64
|
+
}
|
|
65
|
+
const respond = async (req) => {
|
|
66
|
+
if (req.signal?.aborted) {
|
|
67
|
+
throw abortError();
|
|
68
|
+
}
|
|
69
|
+
if (req.tools && req.tools.length > 0) {
|
|
70
|
+
throw new NonRetryableLlmError({
|
|
71
|
+
message: "The chat-completions provider does not support function tools — it serves structured-output requests only. Remove `tools` from the request or use a tool-capable provider.",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
const stageIdMatch = STAGE_ID_MARKER.exec(req.systemPrompt);
|
|
75
|
+
const debugStageId = stageIdMatch ? stageIdMatch[1] : null;
|
|
76
|
+
const convertedSchema = typeboxToJsonSchema(req.outputSchema);
|
|
77
|
+
// Per-request model wins; the provider's configured model (or the
|
|
78
|
+
// `local-coder` default) is the fallback when a request carries none.
|
|
79
|
+
const model = req.model || configuredModel;
|
|
80
|
+
const body = {
|
|
81
|
+
model,
|
|
82
|
+
messages: [
|
|
83
|
+
{ role: "system", content: req.systemPrompt },
|
|
84
|
+
{ role: "user", content: req.userMessage },
|
|
85
|
+
],
|
|
86
|
+
response_format: {
|
|
87
|
+
type: "json_schema",
|
|
88
|
+
json_schema: {
|
|
89
|
+
name: deriveSchemaName(req.outputSchema),
|
|
90
|
+
schema: convertedSchema,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
// Deterministic structured output.
|
|
94
|
+
temperature: 0,
|
|
95
|
+
};
|
|
96
|
+
// `max_tokens`, positive only: 0 means "generate nothing".
|
|
97
|
+
if (req.maxOutputTokens !== undefined && req.maxOutputTokens > 0) {
|
|
98
|
+
body.max_tokens = req.maxOutputTokens;
|
|
99
|
+
}
|
|
100
|
+
debugLlmRequest({
|
|
101
|
+
stageId: debugStageId,
|
|
102
|
+
model,
|
|
103
|
+
maxOutputTokens: req.maxOutputTokens,
|
|
104
|
+
reasoningEffort: req.reasoningEffort,
|
|
105
|
+
systemPromptLen: req.systemPrompt.length,
|
|
106
|
+
userMessageLen: req.userMessage.length,
|
|
107
|
+
systemPromptHead: req.systemPrompt,
|
|
108
|
+
userMessageHead: req.userMessage,
|
|
109
|
+
});
|
|
110
|
+
let response;
|
|
111
|
+
try {
|
|
112
|
+
response = await requestJson({
|
|
113
|
+
url,
|
|
114
|
+
apiKey,
|
|
115
|
+
body,
|
|
116
|
+
fetchImpl,
|
|
117
|
+
signal: req.signal,
|
|
118
|
+
timeoutMs,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
// Honor a mid-flight abort: surface a clean AbortError so
|
|
123
|
+
// `llmStage`'s detector marks the stage skipped rather than
|
|
124
|
+
// failed. Other errors are already framework-classified by
|
|
125
|
+
// `requestJson`; re-throw them.
|
|
126
|
+
if (req.signal?.aborted ||
|
|
127
|
+
(err instanceof Error && err.name === "AbortError")) {
|
|
128
|
+
throw abortError();
|
|
129
|
+
}
|
|
130
|
+
const classified = err instanceof Error ? err : new Error(String(err));
|
|
131
|
+
debugLlmFailure({
|
|
132
|
+
stageId: debugStageId,
|
|
133
|
+
model,
|
|
134
|
+
errorName: classified.name,
|
|
135
|
+
errorMessage: classified.message,
|
|
136
|
+
tokenUsage: { input: 0, output: 0 },
|
|
137
|
+
});
|
|
138
|
+
throw classified;
|
|
139
|
+
}
|
|
140
|
+
const tokenUsage = {
|
|
141
|
+
input: response.usage?.prompt_tokens ?? 0,
|
|
142
|
+
output: response.usage?.completion_tokens ?? 0,
|
|
143
|
+
};
|
|
144
|
+
const text = response.choices?.[0]?.message?.content;
|
|
145
|
+
if (text === undefined || text === "") {
|
|
146
|
+
// Empty structured output is a schema-shaped failure — a re-roll
|
|
147
|
+
// can produce conforming output, so classify it (transient-
|
|
148
|
+
// tagged) `SchemaValidationLlmError`, not a generic transient.
|
|
149
|
+
debugLlmFailure({
|
|
150
|
+
stageId: debugStageId,
|
|
151
|
+
model,
|
|
152
|
+
errorName: "SchemaValidationLlmError",
|
|
153
|
+
errorMessage: "no assistant content",
|
|
154
|
+
tokenUsage,
|
|
155
|
+
});
|
|
156
|
+
throw new SchemaValidationLlmError({
|
|
157
|
+
message: "The chat-completions endpoint returned no assistant content.",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
let parsed;
|
|
161
|
+
try {
|
|
162
|
+
parsed = JSON.parse(text);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
debugLlmFailure({
|
|
166
|
+
stageId: debugStageId,
|
|
167
|
+
model,
|
|
168
|
+
errorName: "SchemaValidationLlmError",
|
|
169
|
+
errorMessage: err instanceof Error ? err.message : String(err),
|
|
170
|
+
rawText: text,
|
|
171
|
+
tokenUsage,
|
|
172
|
+
});
|
|
173
|
+
throw new SchemaValidationLlmError({
|
|
174
|
+
message: `The chat-completions endpoint returned malformed JSON in structured-output content: ${err instanceof Error ? err.message : String(err)}`,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
debugLlmResponse({
|
|
178
|
+
stageId: debugStageId,
|
|
179
|
+
outputTextLen: text.length,
|
|
180
|
+
tokenUsage,
|
|
181
|
+
});
|
|
182
|
+
return {
|
|
183
|
+
output: parsed,
|
|
184
|
+
tokenUsage,
|
|
185
|
+
// A chat-completions response carries no durable response id we
|
|
186
|
+
// surface; `rawResponseId` is optional, so leave it undefined.
|
|
187
|
+
rawResponseId: undefined,
|
|
188
|
+
};
|
|
189
|
+
};
|
|
190
|
+
return { respond };
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/extensions/chat-completions/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { TSchema } from "typebox";
|
|
2
|
+
/**
|
|
3
|
+
* The output shape is intentionally typed as a plain object literal
|
|
4
|
+
* (not a full JSON-Schema TS type). The endpoint's `json_schema`
|
|
5
|
+
* `schema` slot accepts this shape and we round-trip it through the
|
|
6
|
+
* request body. Keeping the return type loose avoids dragging a
|
|
7
|
+
* JSON-Schema dependency into the converter.
|
|
8
|
+
*/
|
|
9
|
+
export type TChatCompletionsJsonSchema = Record<string, unknown>;
|
|
10
|
+
/**
|
|
11
|
+
* Convert a TypeBox schema into a standard JSON Schema document
|
|
12
|
+
* suitable for an OpenAI-compatible `json_schema` response format.
|
|
13
|
+
*
|
|
14
|
+
* Throws `UnsupportedSchemaError` when the source schema contains a
|
|
15
|
+
* TypeBox primitive outside the supported subset.
|
|
16
|
+
*/
|
|
17
|
+
export declare function typeboxToJsonSchema(schema: TSchema): TChatCompletionsJsonSchema;
|
|
18
|
+
//# sourceMappingURL=structured-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../../../src/extensions/chat-completions/structured-output.ts"],"names":[],"mappings":"AA8BA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAEtC;;;;;;GAMG;AACH,MAAM,MAAM,0BAA0B,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AA6BhE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,MAAM,EAAE,OAAO,GAChB,0BAA0B,CA0B5B"}
|