@kaana.ai/contracts 0.0.0-stage → 4.4.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/LICENSE +673 -0
- package/NOTICE +21 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +29 -0
- package/dist/internal/attribution.d.ts +116 -0
- package/dist/internal/attribution.js +82 -0
- package/dist/internal/canonical.d.ts +31 -0
- package/dist/internal/canonical.js +57 -0
- package/dist/internal/catalogue.d.ts +1059 -0
- package/dist/internal/catalogue.js +389 -0
- package/dist/internal/credentialControl.d.ts +329 -0
- package/dist/internal/credentialControl.js +256 -0
- package/dist/internal/decisions.d.ts +245 -0
- package/dist/internal/decisions.js +69 -0
- package/dist/internal/economics.d.ts +27 -0
- package/dist/internal/economics.js +26 -0
- package/dist/internal/embeddings.d.ts +164 -0
- package/dist/internal/embeddings.js +59 -0
- package/dist/internal/errors.d.ts +185 -0
- package/dist/internal/errors.js +211 -0
- package/dist/internal/grant.d.ts +699 -0
- package/dist/internal/grant.js +445 -0
- package/dist/internal/identifiers.d.ts +63 -0
- package/dist/internal/identifiers.js +65 -0
- package/dist/internal/privateAutoExecution.d.ts +158 -0
- package/dist/internal/privateAutoExecution.js +188 -0
- package/dist/internal/realtime.d.ts +1005 -0
- package/dist/internal/realtime.js +187 -0
- package/dist/internal/request.d.ts +1079 -0
- package/dist/internal/request.js +404 -0
- package/dist/internal/routing.d.ts +90 -0
- package/dist/internal/routing.js +139 -0
- package/dist/internal/scopedExecution.d.ts +74 -0
- package/dist/internal/scopedExecution.js +61 -0
- package/dist/internal/settlement.d.ts +205 -0
- package/dist/internal/settlement.js +71 -0
- package/dist/internal/streamEvents.d.ts +422 -0
- package/dist/internal/streamEvents.js +171 -0
- package/dist/internal/usage.d.ts +127 -0
- package/dist/internal/usage.js +109 -0
- package/dist/internal.d.ts +30 -0
- package/dist/internal.js +30 -0
- package/dist/public/audio.d.ts +347 -0
- package/dist/public/audio.js +236 -0
- package/dist/public/audioFormats.d.ts +64 -0
- package/dist/public/audioFormats.js +60 -0
- package/dist/public/catalogue.d.ts +914 -0
- package/dist/public/catalogue.js +516 -0
- package/dist/public/chatCompletions.d.ts +381 -0
- package/dist/public/chatCompletions.js +321 -0
- package/dist/public/decisions.d.ts +182 -0
- package/dist/public/decisions.js +257 -0
- package/dist/public/embeddings.d.ts +34 -0
- package/dist/public/embeddings.js +66 -0
- package/dist/public/errors.d.ts +195 -0
- package/dist/public/errors.js +274 -0
- package/dist/public/generations.d.ts +330 -0
- package/dist/public/generations.js +121 -0
- package/dist/public/identifiers.d.ts +139 -0
- package/dist/public/identifiers.js +160 -0
- package/dist/public/images.d.ts +36 -0
- package/dist/public/images.js +36 -0
- package/dist/public/messages.d.ts +297 -0
- package/dist/public/messages.js +251 -0
- package/dist/public/money.d.ts +308 -0
- package/dist/public/money.js +237 -0
- package/dist/public/policy.d.ts +19 -0
- package/dist/public/policy.js +20 -0
- package/dist/public/realtime.d.ts +2006 -0
- package/dist/public/realtime.js +797 -0
- package/dist/public/responses.d.ts +253 -0
- package/dist/public/responses.js +95 -0
- package/dist/public/streamEvents.d.ts +372 -0
- package/dist/public/streamEvents.js +157 -0
- package/dist/public/version.d.ts +184 -0
- package/dist/public/version.js +184 -0
- package/openapi.json +6967 -0
- package/package.json +44 -4
- package/README.md +0 -3
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The complete error vocabulary Oxy and Kaana exchange, including the codes
|
|
3
|
+
* that name a provider-side failure and never reach a public client.
|
|
4
|
+
*
|
|
5
|
+
* Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/errors.ts`) by Kaana ADR 0002.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { type PublicError, type PublicErrorCode } from '../public/errors.js';
|
|
9
|
+
/**
|
|
10
|
+
* The closed set of inference error codes.
|
|
11
|
+
*
|
|
12
|
+
* Grouped by who must act: the caller (`invalid_request` … `idempotency_conflict`),
|
|
13
|
+
* the account owner (`insufficient_balance`, `spending_limit_exceeded`,
|
|
14
|
+
* `quota_exceeded`, `byok_credential_invalid`), routing/permission policy
|
|
15
|
+
* (`policy_violation`, `commercial_permission_denied`, `no_route_available`),
|
|
16
|
+
* and the platform or its upstreams (everything from `deployment_unavailable`).
|
|
17
|
+
*
|
|
18
|
+
* The platform group is NOT uniformly retryable, and that is the point of
|
|
19
|
+
* `provider_credential_invalid` sitting in it: an upstream that refuses the
|
|
20
|
+
* PLATFORM's own credential fails every identical retry until an operator
|
|
21
|
+
* rotates a key, so classifying it as `provider_error` would send every client
|
|
22
|
+
* into a retry loop against a request that cannot succeed.
|
|
23
|
+
*
|
|
24
|
+
* `provider_billing_refused` is in that group for the same reason and was found
|
|
25
|
+
* the same way — an upstream declining to bill OXY (Anthropic answers 402) has
|
|
26
|
+
* to be distinguishable from the customer's own balance running out, or the
|
|
27
|
+
* error tells them to go and top up an account that is not the one at fault.
|
|
28
|
+
*/
|
|
29
|
+
export declare const INFERENCE_ERROR_CODES: readonly ["invalid_request", "authentication_failed", "permission_denied", "insufficient_scope", "model_not_found", "unsupported_modality", "context_length_exceeded", "request_too_large", "output_limit_exceeded", "idempotency_conflict", "insufficient_balance", "spending_limit_exceeded", "quota_exceeded", "byok_credential_invalid", "policy_violation", "commercial_permission_denied", "no_route_available", "upstream_content_filtered", "cancelled", "rate_limited", "deployment_unavailable", "provider_error", "provider_timeout", "provider_overloaded", "provider_credential_invalid", "provider_billing_refused", "service_unavailable", "internal_error"];
|
|
30
|
+
export declare const inferenceErrorCodeSchema: z.ZodEnum<{
|
|
31
|
+
invalid_request: "invalid_request";
|
|
32
|
+
authentication_failed: "authentication_failed";
|
|
33
|
+
permission_denied: "permission_denied";
|
|
34
|
+
insufficient_scope: "insufficient_scope";
|
|
35
|
+
model_not_found: "model_not_found";
|
|
36
|
+
unsupported_modality: "unsupported_modality";
|
|
37
|
+
context_length_exceeded: "context_length_exceeded";
|
|
38
|
+
request_too_large: "request_too_large";
|
|
39
|
+
output_limit_exceeded: "output_limit_exceeded";
|
|
40
|
+
idempotency_conflict: "idempotency_conflict";
|
|
41
|
+
insufficient_balance: "insufficient_balance";
|
|
42
|
+
spending_limit_exceeded: "spending_limit_exceeded";
|
|
43
|
+
quota_exceeded: "quota_exceeded";
|
|
44
|
+
byok_credential_invalid: "byok_credential_invalid";
|
|
45
|
+
policy_violation: "policy_violation";
|
|
46
|
+
commercial_permission_denied: "commercial_permission_denied";
|
|
47
|
+
no_route_available: "no_route_available";
|
|
48
|
+
upstream_content_filtered: "upstream_content_filtered";
|
|
49
|
+
cancelled: "cancelled";
|
|
50
|
+
rate_limited: "rate_limited";
|
|
51
|
+
provider_error: "provider_error";
|
|
52
|
+
provider_timeout: "provider_timeout";
|
|
53
|
+
provider_overloaded: "provider_overloaded";
|
|
54
|
+
service_unavailable: "service_unavailable";
|
|
55
|
+
internal_error: "internal_error";
|
|
56
|
+
deployment_unavailable: "deployment_unavailable";
|
|
57
|
+
provider_credential_invalid: "provider_credential_invalid";
|
|
58
|
+
provider_billing_refused: "provider_billing_refused";
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Codes for which an identical retried request cannot succeed.
|
|
62
|
+
*
|
|
63
|
+
* `rate_limited` and `quota_exceeded` sit on opposite sides of this line
|
|
64
|
+
* deliberately: a rate limit clears on its own within the window the response
|
|
65
|
+
* names, while a quota is an account-level ceiling that only a human raises.
|
|
66
|
+
* `cancelled` is here because the caller already withdrew the request; a client
|
|
67
|
+
* that retries it is contradicting its own cancellation.
|
|
68
|
+
*
|
|
69
|
+
* `byok_credential_invalid` and `provider_credential_invalid` are the same
|
|
70
|
+
* failure seen from the two sides of the BYOK boundary — the customer's own
|
|
71
|
+
* upstream credential and the platform's — and they are two codes rather than
|
|
72
|
+
* one because only the first names an action the customer can take. Both are
|
|
73
|
+
* non-retryable for the same reason: a credential an upstream has refused keeps
|
|
74
|
+
* being refused until somebody replaces it.
|
|
75
|
+
*
|
|
76
|
+
* `quota_exceeded` and `provider_billing_refused` divide along the same line:
|
|
77
|
+
* both are money, but one is the CUSTOMER's ceiling and the other is Oxy's
|
|
78
|
+
* account with an upstream. Reporting the second as the first is retryability-
|
|
79
|
+
* correct and diagnostically wrong, which is the worst combination — it reads
|
|
80
|
+
* as actionable and the action does nothing.
|
|
81
|
+
*/
|
|
82
|
+
export declare const NON_RETRYABLE_INFERENCE_ERROR_CODES: readonly ["invalid_request", "authentication_failed", "permission_denied", "insufficient_scope", "model_not_found", "unsupported_modality", "context_length_exceeded", "request_too_large", "output_limit_exceeded", "idempotency_conflict", "insufficient_balance", "spending_limit_exceeded", "quota_exceeded", "byok_credential_invalid", "policy_violation", "commercial_permission_denied", "no_route_available", "upstream_content_filtered", "cancelled", "provider_credential_invalid", "provider_billing_refused"];
|
|
83
|
+
/**
|
|
84
|
+
* What the upstream provider said, reduced to the four fields a customer can
|
|
85
|
+
* act on.
|
|
86
|
+
*
|
|
87
|
+
* `.strict()` is the security control here, not a tidiness preference: it means
|
|
88
|
+
* a producer cannot widen this by attaching `requestHeaders`, `curl`, `body` or
|
|
89
|
+
* `raw` and have it silently pass. Adding a field is a contract change with a
|
|
90
|
+
* version bump and a review, which is the point.
|
|
91
|
+
*/
|
|
92
|
+
export declare const providerErrorPassthroughSchema: z.ZodObject<{
|
|
93
|
+
provider: z.ZodString;
|
|
94
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
95
|
+
code: z.ZodOptional<z.ZodString>;
|
|
96
|
+
message: z.ZodOptional<z.ZodString>;
|
|
97
|
+
}, z.core.$strict>;
|
|
98
|
+
/**
|
|
99
|
+
* The error body every inference surface returns and every stream error event
|
|
100
|
+
* carries.
|
|
101
|
+
*
|
|
102
|
+
* `requestId` is always present — an error a customer cannot correlate with a
|
|
103
|
+
* log line is an error they have to reproduce to report.
|
|
104
|
+
*/
|
|
105
|
+
export declare const inferenceErrorSchema: z.ZodObject<{
|
|
106
|
+
schemaVersion: z.ZodLiteral<1>;
|
|
107
|
+
code: z.ZodEnum<{
|
|
108
|
+
invalid_request: "invalid_request";
|
|
109
|
+
authentication_failed: "authentication_failed";
|
|
110
|
+
permission_denied: "permission_denied";
|
|
111
|
+
insufficient_scope: "insufficient_scope";
|
|
112
|
+
model_not_found: "model_not_found";
|
|
113
|
+
unsupported_modality: "unsupported_modality";
|
|
114
|
+
context_length_exceeded: "context_length_exceeded";
|
|
115
|
+
request_too_large: "request_too_large";
|
|
116
|
+
output_limit_exceeded: "output_limit_exceeded";
|
|
117
|
+
idempotency_conflict: "idempotency_conflict";
|
|
118
|
+
insufficient_balance: "insufficient_balance";
|
|
119
|
+
spending_limit_exceeded: "spending_limit_exceeded";
|
|
120
|
+
quota_exceeded: "quota_exceeded";
|
|
121
|
+
byok_credential_invalid: "byok_credential_invalid";
|
|
122
|
+
policy_violation: "policy_violation";
|
|
123
|
+
commercial_permission_denied: "commercial_permission_denied";
|
|
124
|
+
no_route_available: "no_route_available";
|
|
125
|
+
upstream_content_filtered: "upstream_content_filtered";
|
|
126
|
+
cancelled: "cancelled";
|
|
127
|
+
rate_limited: "rate_limited";
|
|
128
|
+
provider_error: "provider_error";
|
|
129
|
+
provider_timeout: "provider_timeout";
|
|
130
|
+
provider_overloaded: "provider_overloaded";
|
|
131
|
+
service_unavailable: "service_unavailable";
|
|
132
|
+
internal_error: "internal_error";
|
|
133
|
+
deployment_unavailable: "deployment_unavailable";
|
|
134
|
+
provider_credential_invalid: "provider_credential_invalid";
|
|
135
|
+
provider_billing_refused: "provider_billing_refused";
|
|
136
|
+
}>;
|
|
137
|
+
message: z.ZodString;
|
|
138
|
+
retryable: z.ZodBoolean;
|
|
139
|
+
requestId: z.ZodString;
|
|
140
|
+
retryAfterMs: z.ZodOptional<z.ZodNumber>;
|
|
141
|
+
param: z.ZodOptional<z.ZodString>;
|
|
142
|
+
upstreamCategory: z.ZodOptional<z.ZodEnum<{
|
|
143
|
+
rate_limit: "rate_limit";
|
|
144
|
+
quota: "quota";
|
|
145
|
+
timeout: "timeout";
|
|
146
|
+
overloaded: "overloaded";
|
|
147
|
+
server_error: "server_error";
|
|
148
|
+
content_filter: "content_filter";
|
|
149
|
+
invalid_request: "invalid_request";
|
|
150
|
+
authentication: "authentication";
|
|
151
|
+
unknown: "unknown";
|
|
152
|
+
}>>;
|
|
153
|
+
providerError: z.ZodOptional<z.ZodObject<{
|
|
154
|
+
provider: z.ZodString;
|
|
155
|
+
status: z.ZodOptional<z.ZodNumber>;
|
|
156
|
+
code: z.ZodOptional<z.ZodString>;
|
|
157
|
+
message: z.ZodOptional<z.ZodString>;
|
|
158
|
+
}, z.core.$strict>>;
|
|
159
|
+
}, z.core.$strip>;
|
|
160
|
+
export type InferenceErrorCode = z.infer<typeof inferenceErrorCodeSchema>;
|
|
161
|
+
export type ProviderErrorPassthrough = z.infer<typeof providerErrorPassthroughSchema>;
|
|
162
|
+
export type InferenceError = z.infer<typeof inferenceErrorSchema>;
|
|
163
|
+
/**
|
|
164
|
+
* The internal codes that never reach a public client, and what the client is
|
|
165
|
+
* told instead. Each names Kaana's own relationship with a provider (a key the
|
|
166
|
+
* provider refused, an account it will not bill, a deployment that is down);
|
|
167
|
+
* on `/v1` Kaana retires that key or route and moves on, and when nothing is
|
|
168
|
+
* left the request is `service_unavailable`, which a later retry can pass once
|
|
169
|
+
* capacity returns.
|
|
170
|
+
*/
|
|
171
|
+
export declare const INTERNAL_ONLY_ERROR_CODES: {
|
|
172
|
+
readonly provider_credential_invalid: "service_unavailable";
|
|
173
|
+
readonly provider_billing_refused: "service_unavailable";
|
|
174
|
+
readonly deployment_unavailable: "service_unavailable";
|
|
175
|
+
};
|
|
176
|
+
/** The public code an internal code is reported as. */
|
|
177
|
+
export declare function toPublicErrorCode(code: InferenceErrorCode): PublicErrorCode;
|
|
178
|
+
/**
|
|
179
|
+
* An internal error as a public client may see it: the provider passthrough is
|
|
180
|
+
* dropped, and an internal-only code becomes `service_unavailable` with a
|
|
181
|
+
* message and category that say nothing about which provider, key or
|
|
182
|
+
* deployment failed. The result is parsed, so a projection that would publish
|
|
183
|
+
* an invalid body throws here rather than reaching a client.
|
|
184
|
+
*/
|
|
185
|
+
export declare function toPublicError(error: InferenceError): PublicError;
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The complete error vocabulary Oxy and Kaana exchange, including the codes
|
|
3
|
+
* that name a provider-side failure and never reach a public client.
|
|
4
|
+
*
|
|
5
|
+
* Moved from `@oxy.so/contracts` 4.12.0 (`src/inference/errors.ts`) by Kaana ADR 0002.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
import { publicErrorSchema, safeErrorTextSchema, upstreamErrorCategorySchema, } from '../public/errors.js';
|
|
9
|
+
import { requestIdSchema } from '../public/identifiers.js';
|
|
10
|
+
import { inferenceProviderSlugSchema } from './identifiers.js';
|
|
11
|
+
/**
|
|
12
|
+
* The closed set of inference error codes.
|
|
13
|
+
*
|
|
14
|
+
* Grouped by who must act: the caller (`invalid_request` … `idempotency_conflict`),
|
|
15
|
+
* the account owner (`insufficient_balance`, `spending_limit_exceeded`,
|
|
16
|
+
* `quota_exceeded`, `byok_credential_invalid`), routing/permission policy
|
|
17
|
+
* (`policy_violation`, `commercial_permission_denied`, `no_route_available`),
|
|
18
|
+
* and the platform or its upstreams (everything from `deployment_unavailable`).
|
|
19
|
+
*
|
|
20
|
+
* The platform group is NOT uniformly retryable, and that is the point of
|
|
21
|
+
* `provider_credential_invalid` sitting in it: an upstream that refuses the
|
|
22
|
+
* PLATFORM's own credential fails every identical retry until an operator
|
|
23
|
+
* rotates a key, so classifying it as `provider_error` would send every client
|
|
24
|
+
* into a retry loop against a request that cannot succeed.
|
|
25
|
+
*
|
|
26
|
+
* `provider_billing_refused` is in that group for the same reason and was found
|
|
27
|
+
* the same way — an upstream declining to bill OXY (Anthropic answers 402) has
|
|
28
|
+
* to be distinguishable from the customer's own balance running out, or the
|
|
29
|
+
* error tells them to go and top up an account that is not the one at fault.
|
|
30
|
+
*/
|
|
31
|
+
export const INFERENCE_ERROR_CODES = [
|
|
32
|
+
'invalid_request',
|
|
33
|
+
'authentication_failed',
|
|
34
|
+
'permission_denied',
|
|
35
|
+
'insufficient_scope',
|
|
36
|
+
'model_not_found',
|
|
37
|
+
'unsupported_modality',
|
|
38
|
+
'context_length_exceeded',
|
|
39
|
+
'request_too_large',
|
|
40
|
+
'output_limit_exceeded',
|
|
41
|
+
'idempotency_conflict',
|
|
42
|
+
'insufficient_balance',
|
|
43
|
+
'spending_limit_exceeded',
|
|
44
|
+
'quota_exceeded',
|
|
45
|
+
'byok_credential_invalid',
|
|
46
|
+
'policy_violation',
|
|
47
|
+
'commercial_permission_denied',
|
|
48
|
+
'no_route_available',
|
|
49
|
+
'upstream_content_filtered',
|
|
50
|
+
'cancelled',
|
|
51
|
+
'rate_limited',
|
|
52
|
+
'deployment_unavailable',
|
|
53
|
+
'provider_error',
|
|
54
|
+
'provider_timeout',
|
|
55
|
+
'provider_overloaded',
|
|
56
|
+
'provider_credential_invalid',
|
|
57
|
+
'provider_billing_refused',
|
|
58
|
+
'service_unavailable',
|
|
59
|
+
'internal_error',
|
|
60
|
+
];
|
|
61
|
+
export const inferenceErrorCodeSchema = z.enum(INFERENCE_ERROR_CODES);
|
|
62
|
+
/**
|
|
63
|
+
* Codes for which an identical retried request cannot succeed.
|
|
64
|
+
*
|
|
65
|
+
* `rate_limited` and `quota_exceeded` sit on opposite sides of this line
|
|
66
|
+
* deliberately: a rate limit clears on its own within the window the response
|
|
67
|
+
* names, while a quota is an account-level ceiling that only a human raises.
|
|
68
|
+
* `cancelled` is here because the caller already withdrew the request; a client
|
|
69
|
+
* that retries it is contradicting its own cancellation.
|
|
70
|
+
*
|
|
71
|
+
* `byok_credential_invalid` and `provider_credential_invalid` are the same
|
|
72
|
+
* failure seen from the two sides of the BYOK boundary — the customer's own
|
|
73
|
+
* upstream credential and the platform's — and they are two codes rather than
|
|
74
|
+
* one because only the first names an action the customer can take. Both are
|
|
75
|
+
* non-retryable for the same reason: a credential an upstream has refused keeps
|
|
76
|
+
* being refused until somebody replaces it.
|
|
77
|
+
*
|
|
78
|
+
* `quota_exceeded` and `provider_billing_refused` divide along the same line:
|
|
79
|
+
* both are money, but one is the CUSTOMER's ceiling and the other is Oxy's
|
|
80
|
+
* account with an upstream. Reporting the second as the first is retryability-
|
|
81
|
+
* correct and diagnostically wrong, which is the worst combination — it reads
|
|
82
|
+
* as actionable and the action does nothing.
|
|
83
|
+
*/
|
|
84
|
+
export const NON_RETRYABLE_INFERENCE_ERROR_CODES = [
|
|
85
|
+
'invalid_request',
|
|
86
|
+
'authentication_failed',
|
|
87
|
+
'permission_denied',
|
|
88
|
+
'insufficient_scope',
|
|
89
|
+
'model_not_found',
|
|
90
|
+
'unsupported_modality',
|
|
91
|
+
'context_length_exceeded',
|
|
92
|
+
'request_too_large',
|
|
93
|
+
'output_limit_exceeded',
|
|
94
|
+
'idempotency_conflict',
|
|
95
|
+
'insufficient_balance',
|
|
96
|
+
'spending_limit_exceeded',
|
|
97
|
+
'quota_exceeded',
|
|
98
|
+
'byok_credential_invalid',
|
|
99
|
+
'policy_violation',
|
|
100
|
+
'commercial_permission_denied',
|
|
101
|
+
'no_route_available',
|
|
102
|
+
'upstream_content_filtered',
|
|
103
|
+
'cancelled',
|
|
104
|
+
'provider_credential_invalid',
|
|
105
|
+
'provider_billing_refused',
|
|
106
|
+
];
|
|
107
|
+
const NON_RETRYABLE_CODE_SET = new Set(NON_RETRYABLE_INFERENCE_ERROR_CODES);
|
|
108
|
+
/**
|
|
109
|
+
* What the upstream provider said, reduced to the four fields a customer can
|
|
110
|
+
* act on.
|
|
111
|
+
*
|
|
112
|
+
* `.strict()` is the security control here, not a tidiness preference: it means
|
|
113
|
+
* a producer cannot widen this by attaching `requestHeaders`, `curl`, `body` or
|
|
114
|
+
* `raw` and have it silently pass. Adding a field is a contract change with a
|
|
115
|
+
* version bump and a review, which is the point.
|
|
116
|
+
*/
|
|
117
|
+
export const providerErrorPassthroughSchema = z
|
|
118
|
+
.object({
|
|
119
|
+
provider: inferenceProviderSlugSchema,
|
|
120
|
+
/** The upstream HTTP status, when the upstream spoke HTTP. */
|
|
121
|
+
status: z.number().int().min(100).max(599).optional(),
|
|
122
|
+
/** The upstream's own error code, verbatim and uninterpreted. */
|
|
123
|
+
code: z.string().max(128).optional(),
|
|
124
|
+
/** The upstream's message, subject to the same credential refusal. */
|
|
125
|
+
message: safeErrorTextSchema.optional(),
|
|
126
|
+
})
|
|
127
|
+
.strict();
|
|
128
|
+
/**
|
|
129
|
+
* The error body every inference surface returns and every stream error event
|
|
130
|
+
* carries.
|
|
131
|
+
*
|
|
132
|
+
* `requestId` is always present — an error a customer cannot correlate with a
|
|
133
|
+
* log line is an error they have to reproduce to report.
|
|
134
|
+
*/
|
|
135
|
+
export const inferenceErrorSchema = z
|
|
136
|
+
.object({
|
|
137
|
+
/** See `version.ts`: this shape appears alone on the wire, so it is versioned. */
|
|
138
|
+
schemaVersion: z.literal(1),
|
|
139
|
+
code: inferenceErrorCodeSchema,
|
|
140
|
+
message: safeErrorTextSchema,
|
|
141
|
+
retryable: z.boolean(),
|
|
142
|
+
requestId: requestIdSchema,
|
|
143
|
+
/** How long to wait before retrying. Only meaningful when `retryable`. */
|
|
144
|
+
retryAfterMs: z.number().int().nonnegative().safe().optional(),
|
|
145
|
+
/** The request field at fault, for `invalid_request`. */
|
|
146
|
+
param: z.string().max(128).optional(),
|
|
147
|
+
/** Present only when an upstream provider was reached and failed. */
|
|
148
|
+
upstreamCategory: upstreamErrorCategorySchema.optional(),
|
|
149
|
+
providerError: providerErrorPassthroughSchema.optional(),
|
|
150
|
+
})
|
|
151
|
+
.superRefine((error, ctx) => {
|
|
152
|
+
if (error.retryable && NON_RETRYABLE_CODE_SET.has(error.code)) {
|
|
153
|
+
ctx.addIssue({
|
|
154
|
+
code: 'custom',
|
|
155
|
+
path: ['retryable'],
|
|
156
|
+
message: `${error.code} can never succeed on an identical retry, so it cannot be retryable`,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
if (error.retryAfterMs !== undefined && !error.retryable) {
|
|
160
|
+
ctx.addIssue({
|
|
161
|
+
code: 'custom',
|
|
162
|
+
path: ['retryAfterMs'],
|
|
163
|
+
message: 'retryAfterMs tells a client when to retry, so it requires retryable: true',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
/* -------------------------------------------------------------------------- */
|
|
168
|
+
/* Projection onto the public vocabulary (Kaana ADR 0002, decision 10) */
|
|
169
|
+
/* -------------------------------------------------------------------------- */
|
|
170
|
+
/**
|
|
171
|
+
* The internal codes that never reach a public client, and what the client is
|
|
172
|
+
* told instead. Each names Kaana's own relationship with a provider (a key the
|
|
173
|
+
* provider refused, an account it will not bill, a deployment that is down);
|
|
174
|
+
* on `/v1` Kaana retires that key or route and moves on, and when nothing is
|
|
175
|
+
* left the request is `service_unavailable`, which a later retry can pass once
|
|
176
|
+
* capacity returns.
|
|
177
|
+
*/
|
|
178
|
+
export const INTERNAL_ONLY_ERROR_CODES = {
|
|
179
|
+
provider_credential_invalid: 'service_unavailable',
|
|
180
|
+
provider_billing_refused: 'service_unavailable',
|
|
181
|
+
deployment_unavailable: 'service_unavailable',
|
|
182
|
+
};
|
|
183
|
+
/** The message a public client reads for a code it is never shown. */
|
|
184
|
+
const UNAVAILABLE_MESSAGE = 'The model is temporarily unavailable. Retry later.';
|
|
185
|
+
/** The public code an internal code is reported as. */
|
|
186
|
+
export function toPublicErrorCode(code) {
|
|
187
|
+
return code in INTERNAL_ONLY_ERROR_CODES
|
|
188
|
+
? INTERNAL_ONLY_ERROR_CODES[code]
|
|
189
|
+
: code;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* An internal error as a public client may see it: the provider passthrough is
|
|
193
|
+
* dropped, and an internal-only code becomes `service_unavailable` with a
|
|
194
|
+
* message and category that say nothing about which provider, key or
|
|
195
|
+
* deployment failed. The result is parsed, so a projection that would publish
|
|
196
|
+
* an invalid body throws here rather than reaching a client.
|
|
197
|
+
*/
|
|
198
|
+
export function toPublicError(error) {
|
|
199
|
+
if (error.code in INTERNAL_ONLY_ERROR_CODES) {
|
|
200
|
+
return publicErrorSchema.parse({
|
|
201
|
+
schemaVersion: 1,
|
|
202
|
+
code: toPublicErrorCode(error.code),
|
|
203
|
+
message: UNAVAILABLE_MESSAGE,
|
|
204
|
+
retryable: true,
|
|
205
|
+
requestId: error.requestId,
|
|
206
|
+
...(error.retryAfterMs === undefined ? {} : { retryAfterMs: error.retryAfterMs }),
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const { providerError: _providerError, ...rest } = error;
|
|
210
|
+
return publicErrorSchema.parse({ ...rest, code: toPublicErrorCode(error.code) });
|
|
211
|
+
}
|