rax-flow-providers 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/claude-adapter.d.ts +41 -0
- package/dist/claude-adapter.d.ts.map +1 -0
- package/dist/claude-adapter.js +236 -0
- package/dist/claude-adapter.js.map +1 -0
- package/dist/cohere-adapter.d.ts +37 -0
- package/dist/cohere-adapter.d.ts.map +1 -0
- package/dist/cohere-adapter.js +160 -0
- package/dist/cohere-adapter.js.map +1 -0
- package/dist/error-mapper.d.ts +51 -0
- package/dist/error-mapper.d.ts.map +1 -0
- package/dist/error-mapper.js +132 -0
- package/dist/error-mapper.js.map +1 -0
- package/dist/gemini-adapter.d.ts +37 -0
- package/dist/gemini-adapter.d.ts.map +1 -0
- package/dist/gemini-adapter.js +150 -0
- package/dist/gemini-adapter.js.map +1 -0
- package/dist/groq-adapter.d.ts +35 -0
- package/dist/groq-adapter.d.ts.map +1 -0
- package/dist/groq-adapter.js +152 -0
- package/dist/groq-adapter.js.map +1 -0
- package/dist/host-bridge-adapter.d.ts +20 -0
- package/dist/host-bridge-adapter.d.ts.map +1 -0
- package/dist/host-bridge-adapter.js +145 -0
- package/dist/host-bridge-adapter.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/mistral-adapter.d.ts +39 -0
- package/dist/mistral-adapter.d.ts.map +1 -0
- package/dist/mistral-adapter.js +172 -0
- package/dist/mistral-adapter.js.map +1 -0
- package/dist/openai-adapter.d.ts +30 -0
- package/dist/openai-adapter.d.ts.map +1 -0
- package/dist/openai-adapter.js +171 -0
- package/dist/openai-adapter.js.map +1 -0
- package/dist/pricing.d.ts +15 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +61 -0
- package/dist/pricing.js.map +1 -0
- package/dist/rest-adapter.d.ts +32 -0
- package/dist/rest-adapter.d.ts.map +1 -0
- package/dist/rest-adapter.js +124 -0
- package/dist/rest-adapter.js.map +1 -0
- package/dist/strategy.d.ts +38 -0
- package/dist/strategy.d.ts.map +1 -0
- package/dist/strategy.js +117 -0
- package/dist/strategy.js.map +1 -0
- package/dist/utils.d.ts +3 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +22 -0
- package/dist/utils.js.map +1 -0
- package/package.json +18 -0
- package/src/claude-adapter.ts +350 -0
- package/src/cohere-adapter.ts +262 -0
- package/src/error-mapper.ts +187 -0
- package/src/gemini-adapter.ts +246 -0
- package/src/groq-adapter.ts +234 -0
- package/src/host-bridge-adapter.ts +189 -0
- package/src/index.ts +11 -0
- package/src/mistral-adapter.ts +262 -0
- package/src/openai-adapter.ts +240 -0
- package/src/pricing.ts +77 -0
- package/src/rest-adapter.ts +181 -0
- package/src/strategy.ts +166 -0
- package/src/utils.ts +18 -0
- package/tsconfig.json +18 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file error-mapper.ts
|
|
3
|
+
* Standardized error taxonomy for all vendor bridge adapters.
|
|
4
|
+
*
|
|
5
|
+
* Every adapter MUST throw a `RaxProviderError` (or a subclass) so that the
|
|
6
|
+
* orchestrator / recovery engine can make uniform decisions on retry,
|
|
7
|
+
* fallback, and escalation without inspecting vendor-specific strings.
|
|
8
|
+
*/
|
|
9
|
+
/** True when the caller should retry the *same* provider. */
|
|
10
|
+
export const RETRYABLE_CODES = new Set([
|
|
11
|
+
"server_error",
|
|
12
|
+
"network_error",
|
|
13
|
+
"timeout",
|
|
14
|
+
"auth_quota_exceeded",
|
|
15
|
+
]);
|
|
16
|
+
/** True when the orchestrator should fall back to a *different* provider. */
|
|
17
|
+
export const FALLBACK_CODES = new Set([
|
|
18
|
+
"auth_invalid",
|
|
19
|
+
"model_not_found",
|
|
20
|
+
"context_length_exceeded",
|
|
21
|
+
"content_filtered",
|
|
22
|
+
"bridge_unavailable",
|
|
23
|
+
"auth_quota_exceeded",
|
|
24
|
+
]);
|
|
25
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
26
|
+
// Error class
|
|
27
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
28
|
+
export class RaxProviderError extends Error {
|
|
29
|
+
/** Machine-readable code driving orchestrator decisions. */
|
|
30
|
+
code;
|
|
31
|
+
/** HTTP status code if the error originated from an HTTP response. */
|
|
32
|
+
httpStatus;
|
|
33
|
+
/** Originating vendor name (e.g. "openai", "gemini"). */
|
|
34
|
+
vendor;
|
|
35
|
+
/** Whether the same provider should be retried. */
|
|
36
|
+
isRetryable;
|
|
37
|
+
/** Whether the orchestrator should fall back to another provider. */
|
|
38
|
+
shouldFallback;
|
|
39
|
+
/** Raw vendor error payload for observability. */
|
|
40
|
+
raw;
|
|
41
|
+
constructor(vendor, code, message, options) {
|
|
42
|
+
super(`[${vendor}] ${code}: ${message}`);
|
|
43
|
+
this.name = "RaxProviderError";
|
|
44
|
+
this.vendor = vendor;
|
|
45
|
+
this.code = code;
|
|
46
|
+
this.httpStatus = options?.httpStatus;
|
|
47
|
+
this.raw = options?.raw;
|
|
48
|
+
this.isRetryable = RETRYABLE_CODES.has(code);
|
|
49
|
+
this.shouldFallback = FALLBACK_CODES.has(code);
|
|
50
|
+
if (options?.cause) {
|
|
51
|
+
this.cause = options.cause;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
56
|
+
// HTTP response → RaxProviderError mapper
|
|
57
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
58
|
+
/**
|
|
59
|
+
* Maps a non-2xx HTTP response to a `RaxProviderError`.
|
|
60
|
+
* Each adapter calls this after detecting `!res.ok`.
|
|
61
|
+
*
|
|
62
|
+
* @param vendor - The adapter name string (e.g. "gemini")
|
|
63
|
+
* @param status - HTTP status code
|
|
64
|
+
* @param body - Pre-parsed response body (may be any shape)
|
|
65
|
+
* @param context - "callModel" | "callStructured" for message clarity
|
|
66
|
+
*/
|
|
67
|
+
export function mapHttpError(vendor, status, body, context) {
|
|
68
|
+
const msg = extractVendorMessage(body);
|
|
69
|
+
let code;
|
|
70
|
+
if (status === 401 || status === 403) {
|
|
71
|
+
code = "auth_invalid";
|
|
72
|
+
}
|
|
73
|
+
else if (status === 429) {
|
|
74
|
+
code = "auth_quota_exceeded";
|
|
75
|
+
}
|
|
76
|
+
else if (status === 404) {
|
|
77
|
+
code = "model_not_found";
|
|
78
|
+
}
|
|
79
|
+
else if (status === 400) {
|
|
80
|
+
// Heuristic sub-classification from message text
|
|
81
|
+
if (/context.length|token.limit|max.context/i.test(msg)) {
|
|
82
|
+
code = "context_length_exceeded";
|
|
83
|
+
}
|
|
84
|
+
else if (/filter|moderat|safety|policy|blocked/i.test(msg)) {
|
|
85
|
+
code = "content_filtered";
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
code = "invalid_request";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
else if (status >= 500) {
|
|
92
|
+
code = "server_error";
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
code = "unknown";
|
|
96
|
+
}
|
|
97
|
+
return new RaxProviderError(vendor, code, `${context} failed — HTTP ${status}: ${msg}`, { httpStatus: status, raw: body });
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Wraps a network-level exception (fetch threw) in a `RaxProviderError`.
|
|
101
|
+
*/
|
|
102
|
+
export function mapNetworkError(vendor, cause) {
|
|
103
|
+
const isTimeout = cause instanceof Error &&
|
|
104
|
+
(cause.name === "AbortError" || /timeout/i.test(cause.message));
|
|
105
|
+
return new RaxProviderError(vendor, isTimeout ? "timeout" : "network_error", cause instanceof Error ? cause.message : String(cause), { cause: cause instanceof Error ? cause : undefined });
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Creates a structured-output parse failure error.
|
|
109
|
+
*/
|
|
110
|
+
export function mapParseError(vendor, context, raw) {
|
|
111
|
+
return new RaxProviderError(vendor, "parse_failed", `${context}: model output could not be parsed as JSON`, { raw });
|
|
112
|
+
}
|
|
113
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
114
|
+
// Internal helpers
|
|
115
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
116
|
+
function extractVendorMessage(body) {
|
|
117
|
+
if (typeof body === "string")
|
|
118
|
+
return body.slice(0, 300);
|
|
119
|
+
if (body && typeof body === "object") {
|
|
120
|
+
const b = body;
|
|
121
|
+
// Common shapes across vendors
|
|
122
|
+
const candidate = b["error"]?.["message"] ??
|
|
123
|
+
b["message"] ??
|
|
124
|
+
b["detail"] ??
|
|
125
|
+
b["error"] ??
|
|
126
|
+
b["msg"];
|
|
127
|
+
if (typeof candidate === "string")
|
|
128
|
+
return candidate.slice(0, 300);
|
|
129
|
+
}
|
|
130
|
+
return "(no message)";
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=error-mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-mapper.js","sourceRoot":"","sources":["../src/error-mapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file gemini-adapter.ts
|
|
3
|
+
* Google Gemini bridge adapter.
|
|
4
|
+
*
|
|
5
|
+
* API reference: https://ai.google.dev/api/generate-content
|
|
6
|
+
*
|
|
7
|
+
* • callModel → POST /v1beta/models/{model}:generateContent
|
|
8
|
+
* • callStructured → same endpoint + responseMimeType "application/json" +
|
|
9
|
+
* responseSchema (Gemini native JSON mode, no prompt hack)
|
|
10
|
+
*
|
|
11
|
+
* Models: gemini-2.0-flash, gemini-2.0-flash-lite, gemini-1.5-pro, etc.
|
|
12
|
+
*/
|
|
13
|
+
import { IModelProvider, ModelResponse, ProviderCallOptions } from "@rax-flow/core";
|
|
14
|
+
export interface GeminiAdapterOptions {
|
|
15
|
+
/** Google AI Studio or Vertex AI API key. */
|
|
16
|
+
apiKey: string;
|
|
17
|
+
/** Override base URL (e.g. for Vertex AI Express endpoints). */
|
|
18
|
+
baseUrl?: string;
|
|
19
|
+
/** Default model to use when none is specified per-call. */
|
|
20
|
+
defaultModel?: string;
|
|
21
|
+
/** Request timeout in ms (default: 30 000). */
|
|
22
|
+
timeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class GeminiAdapter implements IModelProvider {
|
|
25
|
+
private readonly apiKey;
|
|
26
|
+
private readonly baseUrl;
|
|
27
|
+
private readonly defaultModel;
|
|
28
|
+
private readonly timeoutMs;
|
|
29
|
+
constructor(options: GeminiAdapterOptions);
|
|
30
|
+
private endpoint;
|
|
31
|
+
private post;
|
|
32
|
+
private extractText;
|
|
33
|
+
callModel(prompt: string, options?: ProviderCallOptions): Promise<ModelResponse<string>>;
|
|
34
|
+
callStructured<T>(prompt: string, schema: object, options?: ProviderCallOptions): Promise<ModelResponse<T>>;
|
|
35
|
+
healthCheck(): Promise<boolean>;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=gemini-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-adapter.d.ts","sourceRoot":"","sources":["../src/gemini-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAqDpF,MAAM,WAAW,oBAAoB;IACjC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,aAAc,YAAW,cAAc;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,oBAAoB;IASzC,OAAO,CAAC,QAAQ;YAIF,IAAI;IAkDlB,OAAO,CAAC,WAAW;IAQb,SAAS,CACX,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAkC3B,cAAc,CAAC,CAAC,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IA+CtB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAGxC"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file gemini-adapter.ts
|
|
3
|
+
* Google Gemini bridge adapter.
|
|
4
|
+
*
|
|
5
|
+
* API reference: https://ai.google.dev/api/generate-content
|
|
6
|
+
*
|
|
7
|
+
* • callModel → POST /v1beta/models/{model}:generateContent
|
|
8
|
+
* • callStructured → same endpoint + responseMimeType "application/json" +
|
|
9
|
+
* responseSchema (Gemini native JSON mode, no prompt hack)
|
|
10
|
+
*
|
|
11
|
+
* Models: gemini-2.0-flash, gemini-2.0-flash-lite, gemini-1.5-pro, etc.
|
|
12
|
+
*/
|
|
13
|
+
import { parseJsonObjectFromText } from "./utils.js";
|
|
14
|
+
import { RaxProviderError, mapHttpError, mapNetworkError, mapParseError, } from "./error-mapper.js";
|
|
15
|
+
import { calculateCost } from "./pricing.js";
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// Adapter
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
const VENDOR = "gemini";
|
|
20
|
+
const DEFAULT_MODEL = "gemini-2.0-flash";
|
|
21
|
+
const BASE_URL = "https://generativelanguage.googleapis.com";
|
|
22
|
+
export class GeminiAdapter {
|
|
23
|
+
apiKey;
|
|
24
|
+
baseUrl;
|
|
25
|
+
defaultModel;
|
|
26
|
+
timeoutMs;
|
|
27
|
+
constructor(options) {
|
|
28
|
+
this.apiKey = options.apiKey;
|
|
29
|
+
this.baseUrl = options.baseUrl ?? BASE_URL;
|
|
30
|
+
this.defaultModel = options.defaultModel ?? DEFAULT_MODEL;
|
|
31
|
+
this.timeoutMs = options.timeoutMs ?? 30_000;
|
|
32
|
+
}
|
|
33
|
+
// ── private helpers ────────────────────────────────────────────────────────
|
|
34
|
+
endpoint(model, suffix = ":generateContent") {
|
|
35
|
+
return `${this.baseUrl}/v1beta/models/${model}${suffix}?key=${this.apiKey}`;
|
|
36
|
+
}
|
|
37
|
+
async post(url, body) {
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
40
|
+
let res;
|
|
41
|
+
try {
|
|
42
|
+
res = await fetch(url, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { "Content-Type": "application/json" },
|
|
45
|
+
body: JSON.stringify(body),
|
|
46
|
+
signal: controller.signal,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
clearTimeout(timer);
|
|
51
|
+
throw mapNetworkError(VENDOR, err);
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
clearTimeout(timer);
|
|
55
|
+
}
|
|
56
|
+
const raw = await res.json().catch(() => ({}));
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
throw mapHttpError(VENDOR, res.status, raw, "callModel");
|
|
59
|
+
}
|
|
60
|
+
// Gemini sometimes embeds error inside a 200 body
|
|
61
|
+
const payload = raw;
|
|
62
|
+
if (payload.error) {
|
|
63
|
+
throw mapHttpError(VENDOR, payload.error.code ?? 500, payload.error, "callModel");
|
|
64
|
+
}
|
|
65
|
+
// Safety filter rejection
|
|
66
|
+
const finishReason = payload.candidates?.[0]?.finishReason;
|
|
67
|
+
if (finishReason === "SAFETY" || finishReason === "RECITATION") {
|
|
68
|
+
throw new RaxProviderError(VENDOR, "content_filtered", `Generation stopped: ${finishReason}`, { raw: payload });
|
|
69
|
+
}
|
|
70
|
+
return payload;
|
|
71
|
+
}
|
|
72
|
+
extractText(payload) {
|
|
73
|
+
return (payload.candidates?.[0]?.content?.parts?.map((p) => p.text ?? "").join("\n") ?? "");
|
|
74
|
+
}
|
|
75
|
+
// ── IModelProvider ─────────────────────────────────────────────────────────
|
|
76
|
+
async callModel(prompt, options) {
|
|
77
|
+
const started = Date.now();
|
|
78
|
+
const model = options?.model ?? this.defaultModel;
|
|
79
|
+
const body = {
|
|
80
|
+
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
|
81
|
+
generationConfig: {
|
|
82
|
+
temperature: options?.temperature ?? 0.2,
|
|
83
|
+
maxOutputTokens: options?.maxTokens ?? 1200,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
const payload = await this.post(this.endpoint(model), body);
|
|
87
|
+
const output = this.extractText(payload);
|
|
88
|
+
const usage = payload.usageMetadata
|
|
89
|
+
? {
|
|
90
|
+
promptTokens: payload.usageMetadata.promptTokenCount ?? 0,
|
|
91
|
+
completionTokens: payload.usageMetadata.candidatesTokenCount ?? 0,
|
|
92
|
+
totalTokens: payload.usageMetadata.totalTokenCount ?? 0,
|
|
93
|
+
}
|
|
94
|
+
: undefined;
|
|
95
|
+
return {
|
|
96
|
+
provider: VENDOR,
|
|
97
|
+
model,
|
|
98
|
+
latencyMs: Date.now() - started,
|
|
99
|
+
costUsd: calculateCost(model, usage),
|
|
100
|
+
usage,
|
|
101
|
+
output,
|
|
102
|
+
raw: payload,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
async callStructured(prompt, schema, options) {
|
|
106
|
+
const started = Date.now();
|
|
107
|
+
const model = options?.model ?? this.defaultModel;
|
|
108
|
+
// Gemini native JSON mode: responseMimeType + responseSchema
|
|
109
|
+
const body = {
|
|
110
|
+
contents: [{ role: "user", parts: [{ text: prompt }] }],
|
|
111
|
+
generationConfig: {
|
|
112
|
+
temperature: options?.temperature ?? 0,
|
|
113
|
+
maxOutputTokens: options?.maxTokens ?? 1400,
|
|
114
|
+
responseMimeType: "application/json",
|
|
115
|
+
responseSchema: schema,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
// Re-tag the context for HTTP error messages
|
|
119
|
+
const payload = await this.post(this.endpoint(model), body).catch((err) => {
|
|
120
|
+
if (err instanceof RaxProviderError)
|
|
121
|
+
throw err;
|
|
122
|
+
throw mapNetworkError(VENDOR, err);
|
|
123
|
+
});
|
|
124
|
+
const text = this.extractText(payload);
|
|
125
|
+
const parsed = parseJsonObjectFromText(text);
|
|
126
|
+
if (!parsed) {
|
|
127
|
+
throw mapParseError(VENDOR, "callStructured", text);
|
|
128
|
+
}
|
|
129
|
+
const usage = payload.usageMetadata
|
|
130
|
+
? {
|
|
131
|
+
promptTokens: payload.usageMetadata.promptTokenCount ?? 0,
|
|
132
|
+
completionTokens: payload.usageMetadata.candidatesTokenCount ?? 0,
|
|
133
|
+
totalTokens: payload.usageMetadata.totalTokenCount ?? 0,
|
|
134
|
+
}
|
|
135
|
+
: undefined;
|
|
136
|
+
return {
|
|
137
|
+
provider: VENDOR,
|
|
138
|
+
model,
|
|
139
|
+
latencyMs: Date.now() - started,
|
|
140
|
+
costUsd: calculateCost(model, usage),
|
|
141
|
+
usage,
|
|
142
|
+
output: parsed,
|
|
143
|
+
raw: payload,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async healthCheck() {
|
|
147
|
+
return Boolean(this.apiKey && this.baseUrl);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=gemini-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini-adapter.js","sourceRoot":"","sources":["../src/gemini-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACH,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,aAAa,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAqC7C,gFAAgF;AAChF,UAAU;AACV,gFAAgF;AAEhF,MAAM,MAAM,GAAG,QAAQ,CAAC;AACxB,MAAM,aAAa,GAAG,kBAAkB,CAAC;AACzC,MAAM,QAAQ,GAAG,2CAA2C,CAAC;AAa7D,MAAM,OAAO,aAAa;IACL,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,YAAY,CAAS;IACrB,SAAS,CAAS;IAEnC,YAAY,OAA6B;QACrC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,QAAQ,CAAC;QAC3C,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,YAAY,IAAI,aAAa,CAAC;QAC1D,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IACjD,CAAC;IAED,8EAA8E;IAEtE,QAAQ,CAAC,KAAa,EAAE,MAAM,GAAG,kBAAkB;QACvD,OAAO,GAAG,IAAI,CAAC,OAAO,kBAAkB,KAAK,GAAG,MAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC;IAChF,CAAC;IAEO,KAAK,CAAC,IAAI,CAAC,GAAW,EAAE,IAAuB;QACnD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAEnE,IAAI,GAAa,CAAC;QAClB,IAAI,CAAC;YACD,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBACnB,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;gBAC1B,MAAM,EAAE,UAAU,CAAC,MAAM;aAC5B,CAAC,CAAC;QACP,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,CAAC;gBAAS,CAAC;YACP,YAAY,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAE/C,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACV,MAAM,YAAY,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;QAC7D,CAAC;QAED,kDAAkD;QAClD,MAAM,OAAO,GAAG,GAAqB,CAAC;QACtC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAChB,MAAM,YAAY,CACd,MAAM,EACN,OAAO,CAAC,KAAK,CAAC,IAAI,IAAI,GAAG,EACzB,OAAO,CAAC,KAAK,EACb,WAAW,CACd,CAAC;QACN,CAAC;QAED,0BAA0B;QAC1B,MAAM,YAAY,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,CAAC;QAC3D,IAAI,YAAY,KAAK,QAAQ,IAAI,YAAY,KAAK,YAAY,EAAE,CAAC;YAC7D,MAAM,IAAI,gBAAgB,CACtB,MAAM,EACN,kBAAkB,EAClB,uBAAuB,YAAY,EAAE,EACrC,EAAE,GAAG,EAAE,OAAO,EAAE,CACnB,CAAC;QACN,CAAC;QAED,OAAO,OAAO,CAAC;IACnB,CAAC;IAEO,WAAW,CAAC,OAAuB;QACvC,OAAO,CACH,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CACrF,CAAC;IACN,CAAC;IAED,8EAA8E;IAE9E,KAAK,CAAC,SAAS,CACX,MAAc,EACd,OAA6B;QAE7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC;QAElD,MAAM,IAAI,GAAsB;YAC5B,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACvD,gBAAgB,EAAE;gBACd,WAAW,EAAE,OAAO,EAAE,WAAW,IAAI,GAAG;gBACxC,eAAe,EAAE,OAAO,EAAE,SAAS,IAAI,IAAI;aAC9C;SACJ,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QAEzC,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa;YAC/B,CAAC,CAAC;gBACE,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,gBAAgB,IAAI,CAAC;gBACzD,gBAAgB,EAAE,OAAO,CAAC,aAAa,CAAC,oBAAoB,IAAI,CAAC;gBACjE,WAAW,EAAE,OAAO,CAAC,aAAa,CAAC,eAAe,IAAI,CAAC;aAC1D;YACD,CAAC,CAAC,SAAS,CAAC;QAEhB,OAAO;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;YAC/B,OAAO,EAAE,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC;YACpC,KAAK;YACL,MAAM;YACN,GAAG,EAAE,OAAO;SACf,CAAC;IACN,CAAC;IAED,KAAK,CAAC,cAAc,CAChB,MAAc,EACd,MAAc,EACd,OAA6B;QAE7B,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,YAAY,CAAC;QAElD,6DAA6D;QAC7D,MAAM,IAAI,GAAsB;YAC5B,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACvD,gBAAgB,EAAE;gBACd,WAAW,EAAE,OAAO,EAAE,WAAW,IAAI,CAAC;gBACtC,eAAe,EAAE,OAAO,EAAE,SAAS,IAAI,IAAI;gBAC3C,gBAAgB,EAAE,kBAAkB;gBACpC,cAAc,EAAE,MAAM;aACzB;SACJ,CAAC;QAEF,6CAA6C;QAC7C,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACtE,IAAI,GAAG,YAAY,gBAAgB;gBAAE,MAAM,GAAG,CAAC;YAC/C,MAAM,eAAe,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC;QAEH,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,uBAAuB,CAAI,IAAI,CAAC,CAAC;QAEhD,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,aAAa,CAAC,MAAM,EAAE,gBAAgB,EAAE,IAAI,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,CAAC,aAAa;YAC/B,CAAC,CAAC;gBACE,YAAY,EAAE,OAAO,CAAC,aAAa,CAAC,gBAAgB,IAAI,CAAC;gBACzD,gBAAgB,EAAE,OAAO,CAAC,aAAa,CAAC,oBAAoB,IAAI,CAAC;gBACjE,WAAW,EAAE,OAAO,CAAC,aAAa,CAAC,eAAe,IAAI,CAAC;aAC1D;YACD,CAAC,CAAC,SAAS,CAAC;QAEhB,OAAO;YACH,QAAQ,EAAE,MAAM;YAChB,KAAK;YACL,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO;YAC/B,OAAO,EAAE,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC;YACpC,KAAK;YACL,MAAM,EAAE,MAAM;YACd,GAAG,EAAE,OAAO;SACf,CAAC;IACN,CAAC;IAED,KAAK,CAAC,WAAW;QACb,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;CACJ"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file groq-adapter.ts
|
|
3
|
+
* Groq Cloud bridge adapter.
|
|
4
|
+
*
|
|
5
|
+
* API reference: https://console.groq.com/docs/openai
|
|
6
|
+
*
|
|
7
|
+
* Groq exposes an **OpenAI-compatible** REST API, so the wire format is
|
|
8
|
+
* identical to OpenAI. However there are Groq-specific behaviours:
|
|
9
|
+
*
|
|
10
|
+
* • Rate-limit headers: `x-ratelimit-*` (mapped to RaxProviderError)
|
|
11
|
+
* • Structured output: Groq supports `response_format: { type: "json_object" }`
|
|
12
|
+
* but NOT yet OpenAI's strict `json_schema` mode on all models — we use the
|
|
13
|
+
* json_object mode + system prompt enforcement as the safe universal path.
|
|
14
|
+
* • Models: llama-3.3-70b-versatile, llama3-8b-8192, mixtral-8x7b-32768, etc.
|
|
15
|
+
*/
|
|
16
|
+
import { IModelProvider, ModelResponse, ProviderCallOptions } from "@rax-flow/core";
|
|
17
|
+
export interface GroqAdapterOptions {
|
|
18
|
+
apiKey: string;
|
|
19
|
+
baseUrl?: string;
|
|
20
|
+
defaultModel?: string;
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
}
|
|
23
|
+
export declare class GroqAdapter implements IModelProvider {
|
|
24
|
+
private readonly apiKey;
|
|
25
|
+
private readonly baseUrl;
|
|
26
|
+
private readonly defaultModel;
|
|
27
|
+
private readonly timeoutMs;
|
|
28
|
+
constructor(options: GroqAdapterOptions);
|
|
29
|
+
private post;
|
|
30
|
+
private extractText;
|
|
31
|
+
callModel(prompt: string, options?: ProviderCallOptions): Promise<ModelResponse<string>>;
|
|
32
|
+
callStructured<T>(prompt: string, schema: object, options?: ProviderCallOptions): Promise<ModelResponse<T>>;
|
|
33
|
+
healthCheck(): Promise<boolean>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=groq-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groq-adapter.d.ts","sourceRoot":"","sources":["../src/groq-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AA6CpF,MAAM,WAAW,kBAAkB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,WAAY,YAAW,cAAc;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,EAAE,kBAAkB;YASzB,IAAI;IAyDlB,OAAO,CAAC,WAAW;IAMb,SAAS,CACX,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IA8B3B,cAAc,CAAC,CAAC,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,mBAAmB,GAC9B,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IA+CtB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAGxC"}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file groq-adapter.ts
|
|
3
|
+
* Groq Cloud bridge adapter.
|
|
4
|
+
*
|
|
5
|
+
* API reference: https://console.groq.com/docs/openai
|
|
6
|
+
*
|
|
7
|
+
* Groq exposes an **OpenAI-compatible** REST API, so the wire format is
|
|
8
|
+
* identical to OpenAI. However there are Groq-specific behaviours:
|
|
9
|
+
*
|
|
10
|
+
* • Rate-limit headers: `x-ratelimit-*` (mapped to RaxProviderError)
|
|
11
|
+
* • Structured output: Groq supports `response_format: { type: "json_object" }`
|
|
12
|
+
* but NOT yet OpenAI's strict `json_schema` mode on all models — we use the
|
|
13
|
+
* json_object mode + system prompt enforcement as the safe universal path.
|
|
14
|
+
* • Models: llama-3.3-70b-versatile, llama3-8b-8192, mixtral-8x7b-32768, etc.
|
|
15
|
+
*/
|
|
16
|
+
import { parseJsonObjectFromText } from "./utils.js";
|
|
17
|
+
import { RaxProviderError, mapHttpError, mapNetworkError, mapParseError, } from "./error-mapper.js";
|
|
18
|
+
import { calculateCost } from "./pricing.js";
|
|
19
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
+
// Adapter
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
+
const VENDOR = "groq";
|
|
23
|
+
const DEFAULT_MODEL = "llama-3.3-70b-versatile";
|
|
24
|
+
const BASE_URL = "https://api.groq.com/openai/v1";
|
|
25
|
+
export class GroqAdapter {
|
|
26
|
+
apiKey;
|
|
27
|
+
baseUrl;
|
|
28
|
+
defaultModel;
|
|
29
|
+
timeoutMs;
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.apiKey = options.apiKey;
|
|
32
|
+
this.baseUrl = options.baseUrl ?? BASE_URL;
|
|
33
|
+
this.defaultModel = options.defaultModel ?? DEFAULT_MODEL;
|
|
34
|
+
this.timeoutMs = options.timeoutMs ?? 20_000;
|
|
35
|
+
}
|
|
36
|
+
// ── private helpers ────────────────────────────────────────────────────────
|
|
37
|
+
async post(body) {
|
|
38
|
+
const controller = new AbortController();
|
|
39
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
40
|
+
let res;
|
|
41
|
+
try {
|
|
42
|
+
res = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
},
|
|
48
|
+
body: JSON.stringify(body),
|
|
49
|
+
signal: controller.signal,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
clearTimeout(timer);
|
|
54
|
+
throw mapNetworkError(VENDOR, err);
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
clearTimeout(timer);
|
|
58
|
+
}
|
|
59
|
+
const raw = await res.json().catch(() => ({}));
|
|
60
|
+
if (!res.ok) {
|
|
61
|
+
// Groq returns 429 for both rate-limit and daily quota; differentiate
|
|
62
|
+
// by x-ratelimit-remaining-requests header when available
|
|
63
|
+
throw mapHttpError(VENDOR, res.status, raw, "callModel");
|
|
64
|
+
}
|
|
65
|
+
const payload = raw;
|
|
66
|
+
// Groq may embed error inside a 200 (rare but observed during testing)
|
|
67
|
+
if (payload.error) {
|
|
68
|
+
const isQuota = /quota|limit/i.test(payload.error.message ?? "");
|
|
69
|
+
throw new RaxProviderError(VENDOR, isQuota ? "auth_quota_exceeded" : "server_error", payload.error.message ?? "unknown groq error", { raw: payload });
|
|
70
|
+
}
|
|
71
|
+
// Safety / content filter
|
|
72
|
+
const finishReason = payload.choices?.[0]?.finish_reason;
|
|
73
|
+
if (finishReason === "content_filter") {
|
|
74
|
+
throw new RaxProviderError(VENDOR, "content_filtered", "Response blocked by Groq content filter", { raw: payload });
|
|
75
|
+
}
|
|
76
|
+
return payload;
|
|
77
|
+
}
|
|
78
|
+
extractText(payload) {
|
|
79
|
+
return payload.choices?.[0]?.message?.content ?? "";
|
|
80
|
+
}
|
|
81
|
+
// ── IModelProvider ─────────────────────────────────────────────────────────
|
|
82
|
+
async callModel(prompt, options) {
|
|
83
|
+
const started = Date.now();
|
|
84
|
+
const model = options?.model ?? this.defaultModel;
|
|
85
|
+
const payload = await this.post({
|
|
86
|
+
model,
|
|
87
|
+
temperature: options?.temperature ?? 0.2,
|
|
88
|
+
max_tokens: options?.maxTokens ?? 1200,
|
|
89
|
+
messages: [{ role: "user", content: prompt }],
|
|
90
|
+
});
|
|
91
|
+
const usage = payload.usage
|
|
92
|
+
? {
|
|
93
|
+
promptTokens: payload.usage.prompt_tokens ?? 0,
|
|
94
|
+
completionTokens: payload.usage.completion_tokens ?? 0,
|
|
95
|
+
totalTokens: payload.usage.total_tokens ?? 0,
|
|
96
|
+
}
|
|
97
|
+
: undefined;
|
|
98
|
+
return {
|
|
99
|
+
provider: VENDOR,
|
|
100
|
+
model,
|
|
101
|
+
latencyMs: Date.now() - started,
|
|
102
|
+
costUsd: calculateCost(model, usage),
|
|
103
|
+
usage,
|
|
104
|
+
output: this.extractText(payload),
|
|
105
|
+
raw: payload,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
async callStructured(prompt, schema, options) {
|
|
109
|
+
const started = Date.now();
|
|
110
|
+
const model = options?.model ?? this.defaultModel;
|
|
111
|
+
// json_object mode: Groq requires at least one message to mention "JSON"
|
|
112
|
+
const systemMsg = {
|
|
113
|
+
role: "system",
|
|
114
|
+
content: `You MUST respond with valid JSON only.
|
|
115
|
+
Schema to follow strictly:
|
|
116
|
+
${JSON.stringify(schema, null, 2)}
|
|
117
|
+
Do not include any text outside the JSON object.`,
|
|
118
|
+
};
|
|
119
|
+
const payload = await this.post({
|
|
120
|
+
model,
|
|
121
|
+
temperature: options?.temperature ?? 0,
|
|
122
|
+
max_tokens: options?.maxTokens ?? 1400,
|
|
123
|
+
response_format: { type: "json_object" },
|
|
124
|
+
messages: [systemMsg, { role: "user", content: prompt }],
|
|
125
|
+
});
|
|
126
|
+
const text = this.extractText(payload);
|
|
127
|
+
const parsed = parseJsonObjectFromText(text);
|
|
128
|
+
if (!parsed) {
|
|
129
|
+
throw mapParseError(VENDOR, "callStructured", text);
|
|
130
|
+
}
|
|
131
|
+
const usage = payload.usage
|
|
132
|
+
? {
|
|
133
|
+
promptTokens: payload.usage.prompt_tokens ?? 0,
|
|
134
|
+
completionTokens: payload.usage.completion_tokens ?? 0,
|
|
135
|
+
totalTokens: payload.usage.total_tokens ?? 0,
|
|
136
|
+
}
|
|
137
|
+
: undefined;
|
|
138
|
+
return {
|
|
139
|
+
provider: VENDOR,
|
|
140
|
+
model,
|
|
141
|
+
latencyMs: Date.now() - started,
|
|
142
|
+
costUsd: calculateCost(model, usage),
|
|
143
|
+
usage,
|
|
144
|
+
output: parsed,
|
|
145
|
+
raw: payload,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async healthCheck() {
|
|
149
|
+
return Boolean(this.apiKey && this.baseUrl);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=groq-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groq-adapter.js","sourceRoot":"","sources":["../src/groq-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { IModelProvider, ModelResponse, ProviderCallOptions } from "@rax-flow/core";
|
|
2
|
+
interface HostBridgeAdapterOptions {
|
|
3
|
+
model?: string;
|
|
4
|
+
mode?: "auto" | "bridge-only" | "mock";
|
|
5
|
+
command?: string;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class HostBridgeAdapter implements IModelProvider {
|
|
9
|
+
private readonly model;
|
|
10
|
+
private readonly mode;
|
|
11
|
+
private readonly command?;
|
|
12
|
+
private readonly timeoutMs;
|
|
13
|
+
constructor(options?: HostBridgeAdapterOptions);
|
|
14
|
+
private callBridge;
|
|
15
|
+
callModel(prompt: string, options?: ProviderCallOptions): Promise<ModelResponse<string>>;
|
|
16
|
+
callStructured<T>(prompt: string, schema: object, options?: ProviderCallOptions): Promise<ModelResponse<T>>;
|
|
17
|
+
healthCheck(): Promise<boolean>;
|
|
18
|
+
}
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=host-bridge-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"host-bridge-adapter.d.ts","sourceRoot":"","sources":["../src/host-bridge-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAqBpF,UAAU,wBAAwB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,MAAM,CAAC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkED,qBAAa,iBAAkB,YAAW,cAAc;IACtD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAkC;IACvD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,OAAO,GAAE,wBAA6B;YAOpC,UAAU;IAmClB,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAexF,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IA0B3G,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAMtC"}
|