@juspay/neurolink 9.68.15 → 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.
@@ -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 { composeAbortSignals, createTimeoutController, TimeoutError, } from "../utils/timeout.js";
13
- import { emitToolEndFromStepFinish } from "../utils/toolEndEmitter.js";
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";
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
- * Routes Command R / Command R+ chat completions through Cohere's OpenAI-
31
- * compatible endpoint. Embed v3 and Rerank v3 are top-tier for RAG but are
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 BaseProvider {
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
- this.apiKey =
46
- overrideApiKey && overrideApiKey.length > 0
47
- ? overrideApiKey
48
- : getCohereApiKey();
49
- this.baseURL =
50
- credentials?.baseURL ??
51
- process.env.COHERE_BASE_URL ??
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 this.providerName;
43
+ return "cohere";
146
44
  }
147
45
  getDefaultModel() {
148
46
  return getDefaultCohereModel();
149
47
  }
150
- getAISDKModel() {
151
- return this.model;
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" && this.apiKey.trim().length > 0;
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,44 +118,58 @@ 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
- const baseUrl = this.baseURL.replace(/\/compatibility\/v\d+\/?$/, "");
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 response = await fetch(url, {
226
- method: "POST",
227
- headers: {
228
- Authorization: `Bearer ${this.apiKey}`,
229
- "Content-Type": "application/json",
230
- },
231
- body: JSON.stringify({
232
- model,
233
- texts: batch,
234
- input_type: "search_document",
235
- embedding_types: ["float"],
236
- }),
237
- });
238
- if (!response.ok) {
239
- const body = await response.text();
240
- throw this.formatProviderError(new Error(`Cohere /v2/embed failed: ${response.status} — ${body.slice(0, 500)}`));
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
- const json = (await response.json());
243
- const floatVecs = json.embeddings?.float ??
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.68.15",
3
+ "version": "9.68.16",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "Universal AI Development Platform with working MCP integration, multi-provider support, voice (TTS/STT/realtime), and professional CLI. 58+ external MCP servers discoverable, multimodal file processing, RAG pipelines. Build, test, and deploy AI applications with 21+ providers: OpenAI, Anthropic, Google AI Studio, Google Vertex, AWS Bedrock, Azure OpenAI, Mistral, LiteLLM, SageMaker, Hugging Face, Ollama, OpenAI-compatible, OpenRouter, DeepSeek, NVIDIA NIM, LM Studio, llama.cpp, plus voice (OpenAI TTS, ElevenLabs, Deepgram, Azure Speech).",
6
6
  "author": {