@juspay/neurolink 9.68.10 → 9.68.11

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,33 +1,23 @@
1
1
  import type { AIProviderName } from "../constants/enums.js";
2
- import { BaseProvider } from "../core/baseProvider.js";
3
- import type { NeurolinkCredentials, StreamOptions, StreamResult, ValidationSchema } from "../types/index.js";
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
- * Together AI Provider
5
+ * Together AI Provider — direct HTTP, no AI SDK.
7
6
  *
8
7
  * Hosted open-model gateway at api.together.xyz/v1 (OpenAI-compatible).
9
8
  * Llama / Mistral / Qwen / DeepSeek / Gemma / WizardLM available
10
9
  * server-less; pass any catalog id via `--model`.
10
+ * All request/stream/tool-loop orchestration lives in
11
+ * `OpenAIChatCompletionsProvider`; this class only declares configuration
12
+ * and provider-specific error mapping.
11
13
  *
12
14
  * @see https://docs.together.ai/docs/openai-api-compatibility
13
15
  */
14
- export declare class TogetherAIProvider extends BaseProvider {
15
- private model;
16
- private apiKey;
17
- private baseURL;
16
+ export declare class TogetherAIProvider extends OpenAIChatCompletionsProvider {
18
17
  constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["together"]);
19
- protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
20
- private executeStreamInner;
21
18
  protected getProviderName(): AIProviderName;
22
19
  protected getDefaultModel(): string;
23
- protected getAISDKModel(): LanguageModel;
20
+ protected getFallbackModelName(): string;
21
+ protected getFallbackModels(): string[];
24
22
  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
23
  }
33
- export default TogetherAIProvider;
@@ -1,144 +1,60 @@
1
- import { createOpenAI } from "@ai-sdk/openai";
2
1
  import { TogetherAIModels } 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";
11
4
  import { createTogetherAIConfig, 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";
5
+ import { TimeoutError } from "../utils/timeout.js";
6
+ import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
18
7
  const TOGETHER_DEFAULT_BASE_URL = "https://api.together.xyz/v1";
19
8
  const getTogetherApiKey = () => validateApiKey(createTogetherAIConfig());
20
9
  const getDefaultTogetherModel = () => getProviderModel("TOGETHER_MODEL", TogetherAIModels.LLAMA_3_3_70B_INSTRUCT_TURBO);
21
10
  /**
22
- * Together AI Provider
11
+ * Together AI Provider — direct HTTP, no AI SDK.
23
12
  *
24
13
  * Hosted open-model gateway at api.together.xyz/v1 (OpenAI-compatible).
25
14
  * Llama / Mistral / Qwen / DeepSeek / Gemma / WizardLM available
26
15
  * server-less; pass any catalog id via `--model`.
16
+ * All request/stream/tool-loop orchestration lives in
17
+ * `OpenAIChatCompletionsProvider`; this class only declares configuration
18
+ * and provider-specific error mapping.
27
19
  *
28
20
  * @see https://docs.together.ai/docs/openai-api-compatibility
29
21
  */
30
- export class TogetherAIProvider extends BaseProvider {
31
- model;
32
- apiKey;
33
- baseURL;
22
+ export class TogetherAIProvider extends OpenAIChatCompletionsProvider {
34
23
  constructor(modelName, sdk, _region, credentials) {
35
- const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
36
- super(modelName, "together-ai", validatedNeurolink);
37
24
  const overrideApiKey = credentials?.apiKey?.trim();
38
- this.apiKey =
39
- overrideApiKey && overrideApiKey.length > 0
40
- ? overrideApiKey
41
- : getTogetherApiKey();
42
- this.baseURL =
43
- credentials?.baseURL ??
44
- process.env.TOGETHER_BASE_URL ??
45
- TOGETHER_DEFAULT_BASE_URL;
46
- const together = createOpenAI({
47
- apiKey: this.apiKey,
48
- baseURL: this.baseURL,
49
- fetch: createLoggingFetch("together-ai"),
50
- });
51
- this.model = together.chat(this.modelName);
25
+ const apiKey = overrideApiKey && overrideApiKey.length > 0
26
+ ? overrideApiKey
27
+ : getTogetherApiKey();
28
+ const baseURL = credentials?.baseURL?.trim() ||
29
+ process.env.TOGETHER_BASE_URL?.trim() ||
30
+ TOGETHER_DEFAULT_BASE_URL;
31
+ super("together-ai", modelName, sdk, { baseURL, apiKey });
52
32
  logger.debug("Together AI Provider initialized", {
53
33
  modelName: this.modelName,
54
34
  providerName: this.providerName,
55
- baseURL: this.baseURL,
35
+ baseURL: this.config.baseURL,
56
36
  });
57
37
  }
58
- async executeStream(options, _analysisSchema) {
59
- return withClientStreamSpan({
60
- name: "neurolink.provider.stream",
61
- tracer: tracers.provider,
62
- attributes: {
63
- [ATTR.GEN_AI_SYSTEM]: "together-ai",
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?.together;
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
- const shouldUseTools = !options.disableTools && this.supportsTools();
81
- const tools = shouldUseTools
82
- ? options.tools || (await this.getAllTools())
83
- : {};
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("together-ai"),
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("[TogetherAIProvider] 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: `together-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: `together-${Date.now()}` },
127
- };
128
- }
129
- catch (error) {
130
- timeoutController?.cleanup();
131
- throw this.handleProviderError(error);
132
- }
133
- }
134
38
  getProviderName() {
135
- return this.providerName;
39
+ return "together-ai";
136
40
  }
137
41
  getDefaultModel() {
138
42
  return getDefaultTogetherModel();
139
43
  }
140
- getAISDKModel() {
141
- return this.model;
44
+ getFallbackModelName() {
45
+ return TogetherAIModels.LLAMA_3_1_8B_INSTRUCT_TURBO;
46
+ }
47
+ getFallbackModels() {
48
+ return [
49
+ TogetherAIModels.LLAMA_3_3_70B_INSTRUCT_TURBO,
50
+ TogetherAIModels.LLAMA_3_1_405B_INSTRUCT_TURBO,
51
+ TogetherAIModels.LLAMA_3_1_70B_INSTRUCT_TURBO,
52
+ TogetherAIModels.LLAMA_3_1_8B_INSTRUCT_TURBO,
53
+ TogetherAIModels.MIXTRAL_8X22B_INSTRUCT,
54
+ TogetherAIModels.QWEN_2_5_72B_INSTRUCT_TURBO,
55
+ TogetherAIModels.DEEPSEEK_R1,
56
+ TogetherAIModels.DEEPSEEK_V3,
57
+ ];
142
58
  }
143
59
  formatProviderError(error) {
144
60
  if (error instanceof TimeoutError) {
@@ -161,17 +77,5 @@ export class TogetherAIProvider extends BaseProvider {
161
77
  }
162
78
  return new ProviderError(`Together AI error: ${message}`, "together-ai");
163
79
  }
164
- async validateConfiguration() {
165
- return typeof this.apiKey === "string" && this.apiKey.trim().length > 0;
166
- }
167
- getConfiguration() {
168
- return {
169
- provider: this.providerName,
170
- model: this.modelName,
171
- defaultModel: getDefaultTogetherModel(),
172
- baseURL: this.baseURL,
173
- };
174
- }
175
80
  }
176
- export default TogetherAIProvider;
177
81
  //# sourceMappingURL=togetherAi.js.map
@@ -1,33 +1,23 @@
1
1
  import type { AIProviderName } from "../constants/enums.js";
2
- import { BaseProvider } from "../core/baseProvider.js";
3
- import type { NeurolinkCredentials, StreamOptions, StreamResult, ValidationSchema } from "../types/index.js";
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
- * Together AI Provider
5
+ * Together AI Provider — direct HTTP, no AI SDK.
7
6
  *
8
7
  * Hosted open-model gateway at api.together.xyz/v1 (OpenAI-compatible).
9
8
  * Llama / Mistral / Qwen / DeepSeek / Gemma / WizardLM available
10
9
  * server-less; pass any catalog id via `--model`.
10
+ * All request/stream/tool-loop orchestration lives in
11
+ * `OpenAIChatCompletionsProvider`; this class only declares configuration
12
+ * and provider-specific error mapping.
11
13
  *
12
14
  * @see https://docs.together.ai/docs/openai-api-compatibility
13
15
  */
14
- export declare class TogetherAIProvider extends BaseProvider {
15
- private model;
16
- private apiKey;
17
- private baseURL;
16
+ export declare class TogetherAIProvider extends OpenAIChatCompletionsProvider {
18
17
  constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["together"]);
19
- protected executeStream(options: StreamOptions, _analysisSchema?: ValidationSchema): Promise<StreamResult>;
20
- private executeStreamInner;
21
18
  protected getProviderName(): AIProviderName;
22
19
  protected getDefaultModel(): string;
23
- protected getAISDKModel(): LanguageModel;
20
+ protected getFallbackModelName(): string;
21
+ protected getFallbackModels(): string[];
24
22
  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
23
  }
33
- export default TogetherAIProvider;
@@ -1,144 +1,60 @@
1
- import { createOpenAI } from "@ai-sdk/openai";
2
1
  import { TogetherAIModels } 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";
11
4
  import { createTogetherAIConfig, 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";
5
+ import { TimeoutError } from "../utils/timeout.js";
6
+ import { OpenAIChatCompletionsProvider } from "./openaiChatCompletionsBase.js";
18
7
  const TOGETHER_DEFAULT_BASE_URL = "https://api.together.xyz/v1";
19
8
  const getTogetherApiKey = () => validateApiKey(createTogetherAIConfig());
20
9
  const getDefaultTogetherModel = () => getProviderModel("TOGETHER_MODEL", TogetherAIModels.LLAMA_3_3_70B_INSTRUCT_TURBO);
21
10
  /**
22
- * Together AI Provider
11
+ * Together AI Provider — direct HTTP, no AI SDK.
23
12
  *
24
13
  * Hosted open-model gateway at api.together.xyz/v1 (OpenAI-compatible).
25
14
  * Llama / Mistral / Qwen / DeepSeek / Gemma / WizardLM available
26
15
  * server-less; pass any catalog id via `--model`.
16
+ * All request/stream/tool-loop orchestration lives in
17
+ * `OpenAIChatCompletionsProvider`; this class only declares configuration
18
+ * and provider-specific error mapping.
27
19
  *
28
20
  * @see https://docs.together.ai/docs/openai-api-compatibility
29
21
  */
30
- export class TogetherAIProvider extends BaseProvider {
31
- model;
32
- apiKey;
33
- baseURL;
22
+ export class TogetherAIProvider extends OpenAIChatCompletionsProvider {
34
23
  constructor(modelName, sdk, _region, credentials) {
35
- const validatedNeurolink = isNeuroLink(sdk) ? sdk : undefined;
36
- super(modelName, "together-ai", validatedNeurolink);
37
24
  const overrideApiKey = credentials?.apiKey?.trim();
38
- this.apiKey =
39
- overrideApiKey && overrideApiKey.length > 0
40
- ? overrideApiKey
41
- : getTogetherApiKey();
42
- this.baseURL =
43
- credentials?.baseURL ??
44
- process.env.TOGETHER_BASE_URL ??
45
- TOGETHER_DEFAULT_BASE_URL;
46
- const together = createOpenAI({
47
- apiKey: this.apiKey,
48
- baseURL: this.baseURL,
49
- fetch: createLoggingFetch("together-ai"),
50
- });
51
- this.model = together.chat(this.modelName);
25
+ const apiKey = overrideApiKey && overrideApiKey.length > 0
26
+ ? overrideApiKey
27
+ : getTogetherApiKey();
28
+ const baseURL = credentials?.baseURL?.trim() ||
29
+ process.env.TOGETHER_BASE_URL?.trim() ||
30
+ TOGETHER_DEFAULT_BASE_URL;
31
+ super("together-ai", modelName, sdk, { baseURL, apiKey });
52
32
  logger.debug("Together AI Provider initialized", {
53
33
  modelName: this.modelName,
54
34
  providerName: this.providerName,
55
- baseURL: this.baseURL,
35
+ baseURL: this.config.baseURL,
56
36
  });
57
37
  }
58
- async executeStream(options, _analysisSchema) {
59
- return withClientStreamSpan({
60
- name: "neurolink.provider.stream",
61
- tracer: tracers.provider,
62
- attributes: {
63
- [ATTR.GEN_AI_SYSTEM]: "together-ai",
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?.together;
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
- const shouldUseTools = !options.disableTools && this.supportsTools();
81
- const tools = shouldUseTools
82
- ? options.tools || (await this.getAllTools())
83
- : {};
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("together-ai"),
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("[TogetherAIProvider] 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: `together-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: `together-${Date.now()}` },
127
- };
128
- }
129
- catch (error) {
130
- timeoutController?.cleanup();
131
- throw this.handleProviderError(error);
132
- }
133
- }
134
38
  getProviderName() {
135
- return this.providerName;
39
+ return "together-ai";
136
40
  }
137
41
  getDefaultModel() {
138
42
  return getDefaultTogetherModel();
139
43
  }
140
- getAISDKModel() {
141
- return this.model;
44
+ getFallbackModelName() {
45
+ return TogetherAIModels.LLAMA_3_1_8B_INSTRUCT_TURBO;
46
+ }
47
+ getFallbackModels() {
48
+ return [
49
+ TogetherAIModels.LLAMA_3_3_70B_INSTRUCT_TURBO,
50
+ TogetherAIModels.LLAMA_3_1_405B_INSTRUCT_TURBO,
51
+ TogetherAIModels.LLAMA_3_1_70B_INSTRUCT_TURBO,
52
+ TogetherAIModels.LLAMA_3_1_8B_INSTRUCT_TURBO,
53
+ TogetherAIModels.MIXTRAL_8X22B_INSTRUCT,
54
+ TogetherAIModels.QWEN_2_5_72B_INSTRUCT_TURBO,
55
+ TogetherAIModels.DEEPSEEK_R1,
56
+ TogetherAIModels.DEEPSEEK_V3,
57
+ ];
142
58
  }
143
59
  formatProviderError(error) {
144
60
  if (error instanceof TimeoutError) {
@@ -161,16 +77,4 @@ export class TogetherAIProvider extends BaseProvider {
161
77
  }
162
78
  return new ProviderError(`Together AI error: ${message}`, "together-ai");
163
79
  }
164
- async validateConfiguration() {
165
- return typeof this.apiKey === "string" && this.apiKey.trim().length > 0;
166
- }
167
- getConfiguration() {
168
- return {
169
- provider: this.providerName,
170
- model: this.modelName,
171
- defaultModel: getDefaultTogetherModel(),
172
- baseURL: this.baseURL,
173
- };
174
- }
175
80
  }
176
- export default TogetherAIProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "9.68.10",
3
+ "version": "9.68.11",
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": {