@juspay/neurolink 9.68.14 → 9.68.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/browser/neurolink.min.js +344 -344
- package/dist/lib/providers/cohere.d.ts +18 -16
- package/dist/lib/providers/cohere.js +75 -153
- package/dist/lib/providers/groq.d.ts +10 -19
- package/dist/lib/providers/groq.js +29 -126
- package/dist/providers/cohere.d.ts +18 -16
- package/dist/providers/cohere.js +75 -153
- package/dist/providers/groq.d.ts +10 -19
- package/dist/providers/groq.js +29 -126
- package/package.json +1 -1
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import type { AIProviderName } from "../constants/enums.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type { LanguageModel } from "../types/index.js";
|
|
2
|
+
import type { NeurolinkCredentials } from "../types/index.js";
|
|
3
|
+
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
5
4
|
/**
|
|
6
|
-
* Cohere Provider
|
|
5
|
+
* Cohere Provider — direct HTTP, no AI SDK.
|
|
7
6
|
*
|
|
8
|
-
* Routes Command R / Command R+ chat completions through Cohere's
|
|
9
|
-
* compatible endpoint
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Routes Command R / Command R+ chat completions through Cohere's
|
|
8
|
+
* OpenAI-compatible endpoint at /compatibility/v1. All request/stream/
|
|
9
|
+
* tool-loop orchestration lives in `OpenAIChatCompletionsProvider`; this
|
|
10
|
+
* class only declares configuration and provider-specific error mapping.
|
|
11
|
+
*
|
|
12
|
+
* Embed v3 is exposed via `embed()` / `embedMany()` backed by a native
|
|
13
|
+
* POST to /v2/embed (the compatibility path is chat-only).
|
|
12
14
|
*
|
|
13
15
|
* @see https://docs.cohere.com/docs/compatibility-api
|
|
16
|
+
* @see https://docs.cohere.com/reference/embed
|
|
14
17
|
*/
|
|
15
|
-
export declare class CohereProvider extends
|
|
16
|
-
private model;
|
|
17
|
-
private apiKey;
|
|
18
|
-
private baseURL;
|
|
18
|
+
export declare class CohereProvider extends OpenAIChatCompletionsProvider {
|
|
19
19
|
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["cohere"]);
|
|
20
|
-
protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
|
21
|
-
private executeStreamInner;
|
|
22
20
|
protected getProviderName(): AIProviderName;
|
|
23
21
|
protected getDefaultModel(): string;
|
|
24
|
-
protected
|
|
22
|
+
protected getFallbackModelName(): string;
|
|
23
|
+
protected getFallbackModels(): string[];
|
|
25
24
|
protected formatProviderError(error: unknown): Error;
|
|
26
25
|
validateConfiguration(): Promise<boolean>;
|
|
27
26
|
getConfiguration(): {
|
|
@@ -46,7 +45,10 @@ export declare class CohereProvider extends BaseProvider {
|
|
|
46
45
|
/**
|
|
47
46
|
* Batch embedding via Cohere's native /v2/embed endpoint. Cohere caps at
|
|
48
47
|
* 96 inputs per request; larger batches are chunked.
|
|
48
|
+
*
|
|
49
|
+
* Partial failures are not surfaced: if any chunk request fails the whole
|
|
50
|
+
* call rejects and already-embedded chunks are discarded — callers should
|
|
51
|
+
* retry the full input.
|
|
49
52
|
*/
|
|
50
53
|
embedMany(texts: string[], modelName?: string): Promise<number[][]>;
|
|
51
54
|
}
|
|
52
|
-
export default CohereProvider;
|
|
@@ -1,154 +1,61 @@
|
|
|
1
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
2
1
|
import { CohereModels } from "../constants/enums.js";
|
|
3
|
-
import { BaseProvider } from "../core/baseProvider.js";
|
|
4
|
-
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
5
|
-
import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
|
|
6
|
-
import { isNeuroLink } from "../neurolink.js";
|
|
7
|
-
import { createLoggingFetch } from "../utils/loggingFetch.js";
|
|
8
|
-
import { tracers, ATTR, withClientStreamSpan } from "../telemetry/index.js";
|
|
9
2
|
import { AuthenticationError, InvalidModelError, NetworkError, ProviderError, RateLimitError, } from "../types/index.js";
|
|
10
3
|
import { logger } from "../utils/logger.js";
|
|
4
|
+
import { createProxyFetch } from "../proxy/proxyFetch.js";
|
|
11
5
|
import { createCohereConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import { toAnalyticsStreamResult } from "./providerTypeUtils.js";
|
|
16
|
-
import { stepCountIs } from "../utils/tool.js";
|
|
17
|
-
import { streamText } from "../utils/generation.js";
|
|
18
|
-
/**
|
|
19
|
-
* Cohere uses an OpenAI-compatible endpoint at /compatibility/v1 that
|
|
20
|
-
* accepts the same chat-completions shape. Embeddings + Rerank live on
|
|
21
|
-
* the native API and are not exposed through this LLM provider class
|
|
22
|
-
* (use the Cohere SDK directly or the embed/rerank routes when added).
|
|
23
|
-
*/
|
|
6
|
+
import { createTimeoutController, TimeoutError } from "../utils/timeout.js";
|
|
7
|
+
import { stripTrailingSlash } from "./openaiChatCompletionsClient.js";
|
|
8
|
+
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
24
9
|
const COHERE_DEFAULT_BASE_URL = "https://api.cohere.com/compatibility/v1";
|
|
25
10
|
const getCohereApiKey = () => validateApiKey(createCohereConfig());
|
|
26
11
|
const getDefaultCohereModel = () => getProviderModel("COHERE_MODEL", CohereModels.COMMAND_R_PLUS);
|
|
27
12
|
/**
|
|
28
|
-
* Cohere Provider
|
|
13
|
+
* Cohere Provider — direct HTTP, no AI SDK.
|
|
14
|
+
*
|
|
15
|
+
* Routes Command R / Command R+ chat completions through Cohere's
|
|
16
|
+
* OpenAI-compatible endpoint at /compatibility/v1. All request/stream/
|
|
17
|
+
* tool-loop orchestration lives in `OpenAIChatCompletionsProvider`; this
|
|
18
|
+
* class only declares configuration and provider-specific error mapping.
|
|
29
19
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* accessed via the Cohere native SDK / dedicated embedding routes (out of
|
|
33
|
-
* scope for the LLM provider).
|
|
20
|
+
* Embed v3 is exposed via `embed()` / `embedMany()` backed by a native
|
|
21
|
+
* POST to /v2/embed (the compatibility path is chat-only).
|
|
34
22
|
*
|
|
35
23
|
* @see https://docs.cohere.com/docs/compatibility-api
|
|
24
|
+
* @see https://docs.cohere.com/reference/embed
|
|
36
25
|
*/
|
|
37
|
-
export class CohereProvider extends
|
|
38
|
-
model;
|
|
39
|
-
apiKey;
|
|
40
|
-
baseURL;
|
|
26
|
+
export class CohereProvider extends OpenAIChatCompletionsProvider {
|
|
41
27
|
constructor(modelName, sdk, _region, credentials) {
|
|
42
|
-
const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
|
|
43
|
-
super(modelName, "cohere", validatedNeurolink);
|
|
44
28
|
const overrideApiKey = credentials?.apiKey?.trim();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
COHERE_DEFAULT_BASE_URL;
|
|
53
|
-
const cohere = createOpenAI({
|
|
54
|
-
apiKey: this.apiKey,
|
|
55
|
-
baseURL: this.baseURL,
|
|
56
|
-
fetch: createLoggingFetch("cohere"),
|
|
57
|
-
});
|
|
58
|
-
this.model = cohere.chat(this.modelName);
|
|
29
|
+
const apiKey = overrideApiKey && overrideApiKey.length > 0
|
|
30
|
+
? overrideApiKey
|
|
31
|
+
: getCohereApiKey();
|
|
32
|
+
const baseURL = credentials?.baseURL?.trim() ||
|
|
33
|
+
process.env.COHERE_BASE_URL?.trim() ||
|
|
34
|
+
COHERE_DEFAULT_BASE_URL;
|
|
35
|
+
super("cohere", modelName, sdk, { baseURL, apiKey });
|
|
59
36
|
logger.debug("Cohere Provider initialized", {
|
|
60
37
|
modelName: this.modelName,
|
|
61
38
|
providerName: this.providerName,
|
|
62
|
-
baseURL: this.baseURL,
|
|
39
|
+
baseURL: this.config.baseURL,
|
|
63
40
|
});
|
|
64
41
|
}
|
|
65
|
-
async executeStream(options, _analysisSchema) {
|
|
66
|
-
// withClientStreamSpan: keeps the span open until the consumer reaches
|
|
67
|
-
// end-of-stream / error, so the recorded duration reflects the actual
|
|
68
|
-
// stream lifetime instead of just setup.
|
|
69
|
-
return withClientStreamSpan({
|
|
70
|
-
name: "neurolink.provider.stream",
|
|
71
|
-
tracer: tracers.provider,
|
|
72
|
-
attributes: {
|
|
73
|
-
[ATTR.GEN_AI_SYSTEM]: "cohere",
|
|
74
|
-
[ATTR.GEN_AI_MODEL]: this.modelName,
|
|
75
|
-
[ATTR.GEN_AI_OPERATION]: "stream",
|
|
76
|
-
[ATTR.NL_STREAM_MODE]: true,
|
|
77
|
-
},
|
|
78
|
-
}, async () => this.executeStreamInner(options), (r) => r.stream, (r, wrapped) => ({ ...r, stream: wrapped }));
|
|
79
|
-
}
|
|
80
|
-
async executeStreamInner(options) {
|
|
81
|
-
this.validateStreamOptions(options);
|
|
82
|
-
// Resolve per-call credentials first, then fall back to instance-level.
|
|
83
|
-
const perCallCreds = options.credentials?.cohere;
|
|
84
|
-
const effectiveApiKey = perCallCreds?.apiKey?.trim() || this.apiKey;
|
|
85
|
-
const effectiveBaseURL = perCallCreds?.baseURL || this.baseURL;
|
|
86
|
-
const startTime = Date.now();
|
|
87
|
-
const timeout = this.getTimeout(options);
|
|
88
|
-
const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
|
|
89
|
-
try {
|
|
90
|
-
const shouldUseTools = !options.disableTools && this.supportsTools();
|
|
91
|
-
const tools = shouldUseTools
|
|
92
|
-
? options.tools || (await this.getAllTools())
|
|
93
|
-
: {};
|
|
94
|
-
const messages = await this.buildMessagesForStream(options);
|
|
95
|
-
// When per-call credentials differ from instance, build a fresh client.
|
|
96
|
-
const hasDifferentCreds = effectiveApiKey !== this.apiKey || effectiveBaseURL !== this.baseURL;
|
|
97
|
-
const model = hasDifferentCreds
|
|
98
|
-
? createOpenAI({
|
|
99
|
-
apiKey: effectiveApiKey,
|
|
100
|
-
baseURL: effectiveBaseURL,
|
|
101
|
-
fetch: createLoggingFetch("cohere"),
|
|
102
|
-
}).chat(this.modelName)
|
|
103
|
-
: await this.getAISDKModelWithMiddleware(options);
|
|
104
|
-
const result = await streamText({
|
|
105
|
-
model,
|
|
106
|
-
messages,
|
|
107
|
-
temperature: options.temperature,
|
|
108
|
-
maxOutputTokens: options.maxTokens,
|
|
109
|
-
tools,
|
|
110
|
-
stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS),
|
|
111
|
-
toolChoice: resolveToolChoice(options, tools, shouldUseTools),
|
|
112
|
-
abortSignal: composeAbortSignals(options.abortSignal, timeoutController?.controller.signal),
|
|
113
|
-
experimental_telemetry: this.telemetryHandler.getTelemetryConfig(options),
|
|
114
|
-
experimental_repairToolCall: this.getToolCallRepairFn(options),
|
|
115
|
-
onStepFinish: ({ toolCalls, toolResults }) => {
|
|
116
|
-
emitToolEndFromStepFinish(this.neurolink?.getEventEmitter(), toolResults);
|
|
117
|
-
this.handleToolExecutionStorage(toolCalls, toolResults, options, new Date()).catch((error) => {
|
|
118
|
-
logger.warn("[CohereProvider] Failed to store tool executions", {
|
|
119
|
-
provider: this.providerName,
|
|
120
|
-
error: error instanceof Error ? error.message : String(error),
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
},
|
|
124
|
-
});
|
|
125
|
-
timeoutController?.cleanup();
|
|
126
|
-
const transformedStream = this.createTextStream(result);
|
|
127
|
-
const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, toAnalyticsStreamResult(result), Date.now() - startTime, {
|
|
128
|
-
requestId: `cohere-stream-${Date.now()}`,
|
|
129
|
-
streamingMode: true,
|
|
130
|
-
});
|
|
131
|
-
return {
|
|
132
|
-
stream: transformedStream,
|
|
133
|
-
provider: this.providerName,
|
|
134
|
-
model: this.modelName,
|
|
135
|
-
analytics: analyticsPromise,
|
|
136
|
-
metadata: { startTime, streamId: `cohere-${Date.now()}` },
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
timeoutController?.cleanup();
|
|
141
|
-
throw this.handleProviderError(error);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
42
|
getProviderName() {
|
|
145
|
-
return
|
|
43
|
+
return "cohere";
|
|
146
44
|
}
|
|
147
45
|
getDefaultModel() {
|
|
148
46
|
return getDefaultCohereModel();
|
|
149
47
|
}
|
|
150
|
-
|
|
151
|
-
return
|
|
48
|
+
getFallbackModelName() {
|
|
49
|
+
return CohereModels.COMMAND_R;
|
|
50
|
+
}
|
|
51
|
+
getFallbackModels() {
|
|
52
|
+
return [
|
|
53
|
+
CohereModels.COMMAND_A,
|
|
54
|
+
CohereModels.COMMAND_A_REASONING,
|
|
55
|
+
CohereModels.COMMAND_R_PLUS,
|
|
56
|
+
CohereModels.COMMAND_R,
|
|
57
|
+
CohereModels.COMMAND_R7B,
|
|
58
|
+
];
|
|
152
59
|
}
|
|
153
60
|
formatProviderError(error) {
|
|
154
61
|
if (error instanceof TimeoutError) {
|
|
@@ -176,14 +83,15 @@ export class CohereProvider extends BaseProvider {
|
|
|
176
83
|
return new ProviderError(`Cohere error: ${message}`, "cohere");
|
|
177
84
|
}
|
|
178
85
|
async validateConfiguration() {
|
|
179
|
-
return typeof this.apiKey === "string" &&
|
|
86
|
+
return (typeof this.config.apiKey === "string" &&
|
|
87
|
+
this.config.apiKey.trim().length > 0);
|
|
180
88
|
}
|
|
181
89
|
getConfiguration() {
|
|
182
90
|
return {
|
|
183
91
|
provider: this.providerName,
|
|
184
92
|
model: this.modelName,
|
|
185
93
|
defaultModel: getDefaultCohereModel(),
|
|
186
|
-
baseURL: this.baseURL,
|
|
94
|
+
baseURL: this.config.baseURL,
|
|
187
95
|
};
|
|
188
96
|
}
|
|
189
97
|
/**
|
|
@@ -210,45 +118,59 @@ export class CohereProvider extends BaseProvider {
|
|
|
210
118
|
/**
|
|
211
119
|
* Batch embedding via Cohere's native /v2/embed endpoint. Cohere caps at
|
|
212
120
|
* 96 inputs per request; larger batches are chunked.
|
|
121
|
+
*
|
|
122
|
+
* Partial failures are not surfaced: if any chunk request fails the whole
|
|
123
|
+
* call rejects and already-embedded chunks are discarded — callers should
|
|
124
|
+
* retry the full input.
|
|
213
125
|
*/
|
|
214
126
|
async embedMany(texts, modelName) {
|
|
215
127
|
if (texts.length === 0) {
|
|
216
128
|
return [];
|
|
217
129
|
}
|
|
218
130
|
const model = modelName ?? this.getDefaultEmbeddingModel();
|
|
219
|
-
|
|
131
|
+
// Strip the compatibility suffix to reach the native API root.
|
|
132
|
+
const baseUrl = stripTrailingSlash(this.config.baseURL.replace(/\/compatibility\/v\d+\/?$/, ""));
|
|
220
133
|
const url = `${baseUrl}/v2/embed`;
|
|
221
134
|
const BATCH_SIZE = 96;
|
|
222
135
|
const results = [];
|
|
136
|
+
const fetchImpl = createProxyFetch();
|
|
223
137
|
for (let i = 0; i < texts.length; i += BATCH_SIZE) {
|
|
224
138
|
const batch = texts.slice(i, i + BATCH_SIZE);
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
139
|
+
const timeoutController = createTimeoutController(30_000, this.providerName, "generate");
|
|
140
|
+
try {
|
|
141
|
+
const response = await fetchImpl(url, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: {
|
|
144
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
145
|
+
"Content-Type": "application/json",
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
model,
|
|
149
|
+
texts: batch,
|
|
150
|
+
input_type: "search_document",
|
|
151
|
+
embedding_types: ["float"],
|
|
152
|
+
}),
|
|
153
|
+
...(timeoutController?.controller.signal
|
|
154
|
+
? { signal: timeoutController.controller.signal }
|
|
155
|
+
: {}),
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok) {
|
|
158
|
+
const body = await response.text().catch(() => "");
|
|
159
|
+
throw this.formatProviderError(new Error(`Cohere /v2/embed failed: ${response.status} — ${body.slice(0, 500)}`));
|
|
160
|
+
}
|
|
161
|
+
const json = (await response.json());
|
|
162
|
+
const floatVecs = json.embeddings?.float ??
|
|
163
|
+
(Array.isArray(json.embeddings) ? json.embeddings : undefined);
|
|
164
|
+
if (!floatVecs || floatVecs.length !== batch.length) {
|
|
165
|
+
throw new ProviderError(`Cohere /v2/embed returned ${floatVecs?.length ?? 0} embeddings for ${batch.length} inputs.`, "cohere");
|
|
166
|
+
}
|
|
167
|
+
results.push(...floatVecs);
|
|
241
168
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
(Array.isArray(json.embeddings) ? json.embeddings : undefined);
|
|
245
|
-
if (!floatVecs || floatVecs.length !== batch.length) {
|
|
246
|
-
throw new ProviderError(`Cohere /v2/embed returned ${floatVecs?.length ?? 0} embeddings for ${batch.length} inputs.`, "cohere");
|
|
169
|
+
finally {
|
|
170
|
+
timeoutController?.cleanup();
|
|
247
171
|
}
|
|
248
|
-
results.push(...floatVecs);
|
|
249
172
|
}
|
|
250
173
|
return results;
|
|
251
174
|
}
|
|
252
175
|
}
|
|
253
|
-
export default CohereProvider;
|
|
254
176
|
//# sourceMappingURL=cohere.js.map
|
|
@@ -1,33 +1,24 @@
|
|
|
1
1
|
import type { AIProviderName } from "../constants/enums.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type { LanguageModel } from "../types/index.js";
|
|
2
|
+
import type { NeurolinkCredentials } from "../types/index.js";
|
|
3
|
+
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
5
4
|
/**
|
|
6
|
-
* Groq Provider
|
|
5
|
+
* Groq Provider — direct HTTP, no AI SDK.
|
|
7
6
|
*
|
|
8
7
|
* Sub-100ms inference of Llama / Mistral / Gemma at api.groq.com/openai/v1
|
|
9
8
|
* (OpenAI-compatible). Best for low-latency tier; trade-off vs other open
|
|
10
9
|
* model hosts is throughput latency, not quality.
|
|
11
10
|
*
|
|
11
|
+
* All request/stream/tool-loop orchestration lives in
|
|
12
|
+
* `OpenAIChatCompletionsProvider`; this class only declares configuration
|
|
13
|
+
* and provider-specific error mapping.
|
|
14
|
+
*
|
|
12
15
|
* @see https://console.groq.com/docs/quickstart
|
|
13
16
|
*/
|
|
14
|
-
export declare class GroqProvider extends
|
|
15
|
-
private model;
|
|
16
|
-
private apiKey;
|
|
17
|
-
private baseURL;
|
|
17
|
+
export declare class GroqProvider extends OpenAIChatCompletionsProvider {
|
|
18
18
|
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["groq"]);
|
|
19
|
-
protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
|
20
|
-
private executeStreamInner;
|
|
21
19
|
protected getProviderName(): AIProviderName;
|
|
22
20
|
protected getDefaultModel(): string;
|
|
23
|
-
protected
|
|
21
|
+
protected getFallbackModelName(): string;
|
|
22
|
+
protected getFallbackModels(): string[];
|
|
24
23
|
protected formatProviderError(error: unknown): Error;
|
|
25
|
-
validateConfiguration(): Promise<boolean>;
|
|
26
|
-
getConfiguration(): {
|
|
27
|
-
provider: AIProviderName;
|
|
28
|
-
model: string;
|
|
29
|
-
defaultModel: string;
|
|
30
|
-
baseURL: string;
|
|
31
|
-
};
|
|
32
24
|
}
|
|
33
|
-
export default GroqProvider;
|
|
@@ -1,144 +1,59 @@
|
|
|
1
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
2
1
|
import { GroqModels } from "../constants/enums.js";
|
|
3
|
-
import { BaseProvider } from "../core/baseProvider.js";
|
|
4
|
-
import { DEFAULT_MAX_STEPS } from "../core/constants.js";
|
|
5
|
-
import { streamAnalyticsCollector } from "../core/streamAnalytics.js";
|
|
6
|
-
import { isNeuroLink } from "../neurolink.js";
|
|
7
|
-
import { createLoggingFetch } from "../utils/loggingFetch.js";
|
|
8
|
-
import { tracers, ATTR, withClientStreamSpan } from "../telemetry/index.js";
|
|
9
2
|
import { AuthenticationError, InvalidModelError, ProviderError, RateLimitError, } from "../types/index.js";
|
|
10
3
|
import { logger } from "../utils/logger.js";
|
|
11
4
|
import { createGroqConfig, getProviderModel, validateApiKey, } from "../utils/providerConfig.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { resolveToolChoice } from "../utils/toolChoice.js";
|
|
15
|
-
import { toAnalyticsStreamResult } from "./providerTypeUtils.js";
|
|
16
|
-
import { stepCountIs } from "../utils/tool.js";
|
|
17
|
-
import { streamText } from "../utils/generation.js";
|
|
5
|
+
import { TimeoutError } from "../utils/timeout.js";
|
|
6
|
+
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
18
7
|
const GROQ_DEFAULT_BASE_URL = "https://api.groq.com/openai/v1";
|
|
19
8
|
const getGroqApiKey = () => validateApiKey(createGroqConfig());
|
|
20
9
|
const getDefaultGroqModel = () => getProviderModel("GROQ_MODEL", GroqModels.LLAMA_3_3_70B_VERSATILE);
|
|
21
10
|
/**
|
|
22
|
-
* Groq Provider
|
|
11
|
+
* Groq Provider — direct HTTP, no AI SDK.
|
|
23
12
|
*
|
|
24
13
|
* Sub-100ms inference of Llama / Mistral / Gemma at api.groq.com/openai/v1
|
|
25
14
|
* (OpenAI-compatible). Best for low-latency tier; trade-off vs other open
|
|
26
15
|
* model hosts is throughput latency, not quality.
|
|
27
16
|
*
|
|
17
|
+
* All request/stream/tool-loop orchestration lives in
|
|
18
|
+
* `OpenAIChatCompletionsProvider`; this class only declares configuration
|
|
19
|
+
* and provider-specific error mapping.
|
|
20
|
+
*
|
|
28
21
|
* @see https://console.groq.com/docs/quickstart
|
|
29
22
|
*/
|
|
30
|
-
export class GroqProvider extends
|
|
31
|
-
model;
|
|
32
|
-
apiKey;
|
|
33
|
-
baseURL;
|
|
23
|
+
export class GroqProvider extends OpenAIChatCompletionsProvider {
|
|
34
24
|
constructor(modelName, sdk, _region, credentials) {
|
|
35
|
-
const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
|
|
36
|
-
super(modelName, "groq", validatedNeurolink);
|
|
37
25
|
const overrideApiKey = credentials?.apiKey?.trim();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
GROQ_DEFAULT_BASE_URL;
|
|
46
|
-
const groq = createOpenAI({
|
|
47
|
-
apiKey: this.apiKey,
|
|
48
|
-
baseURL: this.baseURL,
|
|
49
|
-
fetch: createLoggingFetch("groq"),
|
|
50
|
-
});
|
|
51
|
-
this.model = groq.chat(this.modelName);
|
|
26
|
+
const apiKey = overrideApiKey && overrideApiKey.length > 0
|
|
27
|
+
? overrideApiKey
|
|
28
|
+
: getGroqApiKey();
|
|
29
|
+
const baseURL = credentials?.baseURL?.trim() ||
|
|
30
|
+
process.env.GROQ_BASE_URL?.trim() ||
|
|
31
|
+
GROQ_DEFAULT_BASE_URL;
|
|
32
|
+
super("groq", modelName, sdk, { baseURL, apiKey });
|
|
52
33
|
logger.debug("Groq Provider initialized", {
|
|
53
34
|
modelName: this.modelName,
|
|
54
35
|
providerName: this.providerName,
|
|
55
|
-
baseURL: this.baseURL,
|
|
36
|
+
baseURL: this.config.baseURL,
|
|
56
37
|
});
|
|
57
38
|
}
|
|
58
|
-
async executeStream(options, _analysisSchema) {
|
|
59
|
-
return withClientStreamSpan({
|
|
60
|
-
name: "neurolink.provider.stream",
|
|
61
|
-
tracer: tracers.provider,
|
|
62
|
-
attributes: {
|
|
63
|
-
[ATTR.GEN_AI_SYSTEM]: "groq",
|
|
64
|
-
[ATTR.GEN_AI_MODEL]: this.modelName,
|
|
65
|
-
[ATTR.GEN_AI_OPERATION]: "stream",
|
|
66
|
-
[ATTR.NL_STREAM_MODE]: true,
|
|
67
|
-
},
|
|
68
|
-
}, async () => this.executeStreamInner(options), (r) => r.stream, (r, wrapped) => ({ ...r, stream: wrapped }));
|
|
69
|
-
}
|
|
70
|
-
async executeStreamInner(options) {
|
|
71
|
-
this.validateStreamOptions(options);
|
|
72
|
-
// Resolve per-call credentials first, then fall back to instance-level.
|
|
73
|
-
const perCallCreds = options.credentials?.groq;
|
|
74
|
-
const effectiveApiKey = perCallCreds?.apiKey?.trim() || this.apiKey;
|
|
75
|
-
const effectiveBaseURL = perCallCreds?.baseURL || this.baseURL;
|
|
76
|
-
const startTime = Date.now();
|
|
77
|
-
const timeout = this.getTimeout(options);
|
|
78
|
-
const timeoutController = createTimeoutController(timeout, this.providerName, "stream");
|
|
79
|
-
try {
|
|
80
|
-
// Use the canonical BaseProvider helper: merges base tools (MCP/built-in)
|
|
81
|
-
// with user-provided tools (RAG, etc.) and applies per-call filtering.
|
|
82
|
-
const shouldUseTools = !options.disableTools && this.supportsTools();
|
|
83
|
-
const tools = await this.getToolsForStream(options);
|
|
84
|
-
const messages = await this.buildMessagesForStream(options);
|
|
85
|
-
// When per-call credentials differ from instance, build a fresh client.
|
|
86
|
-
const hasDifferentCreds = effectiveApiKey !== this.apiKey || effectiveBaseURL !== this.baseURL;
|
|
87
|
-
const model = hasDifferentCreds
|
|
88
|
-
? createOpenAI({
|
|
89
|
-
apiKey: effectiveApiKey,
|
|
90
|
-
baseURL: effectiveBaseURL,
|
|
91
|
-
fetch: createLoggingFetch("groq"),
|
|
92
|
-
}).chat(this.modelName)
|
|
93
|
-
: await this.getAISDKModelWithMiddleware(options);
|
|
94
|
-
const result = await streamText({
|
|
95
|
-
model,
|
|
96
|
-
messages,
|
|
97
|
-
temperature: options.temperature,
|
|
98
|
-
maxOutputTokens: options.maxTokens,
|
|
99
|
-
tools,
|
|
100
|
-
stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS),
|
|
101
|
-
toolChoice: resolveToolChoice(options, tools, shouldUseTools),
|
|
102
|
-
abortSignal: composeAbortSignals(options.abortSignal, timeoutController?.controller.signal),
|
|
103
|
-
experimental_telemetry: this.telemetryHandler.getTelemetryConfig(options),
|
|
104
|
-
experimental_repairToolCall: this.getToolCallRepairFn(options),
|
|
105
|
-
onStepFinish: ({ toolCalls, toolResults }) => {
|
|
106
|
-
emitToolEndFromStepFinish(this.neurolink?.getEventEmitter(), toolResults);
|
|
107
|
-
this.handleToolExecutionStorage(toolCalls, toolResults, options, new Date()).catch((error) => {
|
|
108
|
-
logger.warn("[GroqProvider] Failed to store tool executions", {
|
|
109
|
-
provider: this.providerName,
|
|
110
|
-
error: error instanceof Error ? error.message : String(error),
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
timeoutController?.cleanup();
|
|
116
|
-
const transformedStream = this.createTextStream(result);
|
|
117
|
-
const analyticsPromise = streamAnalyticsCollector.createAnalytics(this.providerName, this.modelName, toAnalyticsStreamResult(result), Date.now() - startTime, {
|
|
118
|
-
requestId: `groq-stream-${Date.now()}`,
|
|
119
|
-
streamingMode: true,
|
|
120
|
-
});
|
|
121
|
-
return {
|
|
122
|
-
stream: transformedStream,
|
|
123
|
-
provider: this.providerName,
|
|
124
|
-
model: this.modelName,
|
|
125
|
-
analytics: analyticsPromise,
|
|
126
|
-
metadata: { startTime, streamId: `groq-${Date.now()}` },
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
timeoutController?.cleanup();
|
|
131
|
-
throw this.handleProviderError(error);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
39
|
getProviderName() {
|
|
135
|
-
return
|
|
40
|
+
return "groq";
|
|
136
41
|
}
|
|
137
42
|
getDefaultModel() {
|
|
138
43
|
return getDefaultGroqModel();
|
|
139
44
|
}
|
|
140
|
-
|
|
141
|
-
return
|
|
45
|
+
getFallbackModelName() {
|
|
46
|
+
return GroqModels.LLAMA_3_1_8B_INSTANT;
|
|
47
|
+
}
|
|
48
|
+
getFallbackModels() {
|
|
49
|
+
return [
|
|
50
|
+
GroqModels.LLAMA_3_3_70B_VERSATILE,
|
|
51
|
+
GroqModels.LLAMA_3_1_8B_INSTANT,
|
|
52
|
+
GroqModels.GEMMA_2_9B_IT,
|
|
53
|
+
GroqModels.MIXTRAL_8X7B_32768,
|
|
54
|
+
GroqModels.LLAMA_3_2_90B_VISION_PREVIEW,
|
|
55
|
+
GroqModels.LLAMA_3_2_11B_VISION_PREVIEW,
|
|
56
|
+
];
|
|
142
57
|
}
|
|
143
58
|
formatProviderError(error) {
|
|
144
59
|
if (error instanceof TimeoutError) {
|
|
@@ -166,17 +81,5 @@ export class GroqProvider extends BaseProvider {
|
|
|
166
81
|
}
|
|
167
82
|
return new ProviderError(`Groq error: ${message}`, "groq");
|
|
168
83
|
}
|
|
169
|
-
async validateConfiguration() {
|
|
170
|
-
return typeof this.apiKey === "string" && this.apiKey.trim().length > 0;
|
|
171
|
-
}
|
|
172
|
-
getConfiguration() {
|
|
173
|
-
return {
|
|
174
|
-
provider: this.providerName,
|
|
175
|
-
model: this.modelName,
|
|
176
|
-
defaultModel: getDefaultGroqModel(),
|
|
177
|
-
baseURL: this.baseURL,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
84
|
}
|
|
181
|
-
export default GroqProvider;
|
|
182
85
|
//# sourceMappingURL=groq.js.map
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import type { AIProviderName } from "../constants/enums.js";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import type { LanguageModel } from "../types/index.js";
|
|
2
|
+
import type { NeurolinkCredentials } from "../types/index.js";
|
|
3
|
+
import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
|
|
5
4
|
/**
|
|
6
|
-
* Cohere Provider
|
|
5
|
+
* Cohere Provider — direct HTTP, no AI SDK.
|
|
7
6
|
*
|
|
8
|
-
* Routes Command R / Command R+ chat completions through Cohere's
|
|
9
|
-
* compatible endpoint
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* Routes Command R / Command R+ chat completions through Cohere's
|
|
8
|
+
* OpenAI-compatible endpoint at /compatibility/v1. All request/stream/
|
|
9
|
+
* tool-loop orchestration lives in `OpenAIChatCompletionsProvider`; this
|
|
10
|
+
* class only declares configuration and provider-specific error mapping.
|
|
11
|
+
*
|
|
12
|
+
* Embed v3 is exposed via `embed()` / `embedMany()` backed by a native
|
|
13
|
+
* POST to /v2/embed (the compatibility path is chat-only).
|
|
12
14
|
*
|
|
13
15
|
* @see https://docs.cohere.com/docs/compatibility-api
|
|
16
|
+
* @see https://docs.cohere.com/reference/embed
|
|
14
17
|
*/
|
|
15
|
-
export declare class CohereProvider extends
|
|
16
|
-
private model;
|
|
17
|
-
private apiKey;
|
|
18
|
-
private baseURL;
|
|
18
|
+
export declare class CohereProvider extends OpenAIChatCompletionsProvider {
|
|
19
19
|
constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["cohere"]);
|
|
20
|
-
protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
|
|
21
|
-
private executeStreamInner;
|
|
22
20
|
protected getProviderName(): AIProviderName;
|
|
23
21
|
protected getDefaultModel(): string;
|
|
24
|
-
protected
|
|
22
|
+
protected getFallbackModelName(): string;
|
|
23
|
+
protected getFallbackModels(): string[];
|
|
25
24
|
protected formatProviderError(error: unknown): Error;
|
|
26
25
|
validateConfiguration(): Promise<boolean>;
|
|
27
26
|
getConfiguration(): {
|
|
@@ -46,7 +45,10 @@ export declare class CohereProvider extends BaseProvider {
|
|
|
46
45
|
/**
|
|
47
46
|
* Batch embedding via Cohere's native /v2/embed endpoint. Cohere caps at
|
|
48
47
|
* 96 inputs per request; larger batches are chunked.
|
|
48
|
+
*
|
|
49
|
+
* Partial failures are not surfaced: if any chunk request fails the whole
|
|
50
|
+
* call rejects and already-embedded chunks are discarded — callers should
|
|
51
|
+
* retry the full input.
|
|
49
52
|
*/
|
|
50
53
|
embedMany(texts: string[], modelName?: string): Promise<number[][]>;
|
|
51
54
|
}
|
|
52
|
-
export default CohereProvider;
|