@nocobase/plugin-ai 2.2.0-alpha.6 → 2.2.0-alpha.7

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.
Files changed (47) hide show
  1. package/dist/ai/docs/nocobase/file-manager/storage/local.md +3 -1
  2. package/dist/ai/docs/nocobase/security/guide.md +23 -0
  3. package/dist/ai/tools/subAgentWebSearch.js +20 -15
  4. package/dist/client/index.js +1 -1
  5. package/dist/client-v2/ai-employees/tools/data-modeling/useFieldInterfaceOptions.d.ts +1 -0
  6. package/dist/client-v2/index.js +1 -1
  7. package/dist/client-v2/llm-providers/forms.d.ts +2 -0
  8. package/dist/client-v2/llm-providers/index.d.ts +1 -0
  9. package/dist/externalVersion.js +15 -15
  10. package/dist/locale/en-US.json +5 -0
  11. package/dist/locale/zh-CN.json +5 -0
  12. package/dist/node_modules/@langchain/mistralai/package.json +1 -1
  13. package/dist/node_modules/@langchain/xai/package.json +1 -1
  14. package/dist/node_modules/fs-extra/package.json +1 -1
  15. package/dist/node_modules/jsonrepair/package.json +1 -1
  16. package/dist/node_modules/just-bash/package.json +1 -1
  17. package/dist/node_modules/nodejs-snowflake/package.json +1 -1
  18. package/dist/node_modules/openai/package.json +1 -1
  19. package/dist/node_modules/zod/package.json +1 -1
  20. package/dist/server/ai-employees/ai-employee.js +2 -1
  21. package/dist/server/ai-employees/prompts.d.ts +2 -1
  22. package/dist/server/ai-employees/prompts.js +8 -2
  23. package/dist/server/collections/ai-usage-events.d.ts +10 -0
  24. package/dist/server/collections/ai-usage-events.js +183 -0
  25. package/dist/server/llm-providers/dashscope.d.ts +2 -1
  26. package/dist/server/llm-providers/dashscope.js +16 -1
  27. package/dist/server/llm-providers/deepseek.d.ts +2 -1
  28. package/dist/server/llm-providers/deepseek.js +33 -2
  29. package/dist/server/llm-providers/kimi/provider.d.ts +2 -1
  30. package/dist/server/llm-providers/kimi/provider.js +22 -2
  31. package/dist/server/llm-providers/mistral.d.ts +36 -2
  32. package/dist/server/llm-providers/mistral.js +62 -2
  33. package/dist/server/llm-providers/openai/completions.d.ts +2 -1
  34. package/dist/server/llm-providers/openai/completions.js +17 -1
  35. package/dist/server/llm-providers/openai/responses.d.ts +2 -1
  36. package/dist/server/llm-providers/openai/responses.js +17 -1
  37. package/dist/server/llm-providers/orcarouter.d.ts +35 -0
  38. package/dist/server/llm-providers/orcarouter.js +155 -0
  39. package/dist/server/llm-providers/provider.d.ts +7 -0
  40. package/dist/server/llm-providers/provider.js +7 -1
  41. package/dist/server/manager/ai-chat-conversation.js +2 -0
  42. package/dist/server/manager/ai-manager.d.ts +2 -1
  43. package/dist/server/manager/ai-manager.js +4 -1
  44. package/dist/server/manager/ai-usage-events.d.ts +49 -0
  45. package/dist/server/manager/ai-usage-events.js +195 -0
  46. package/dist/server/plugin.js +2 -0
  47. package/package.json +2 -2
@@ -45,6 +45,7 @@ var import_provider = require("./provider");
45
45
  var import_ai_manager = require("../manager/ai-manager");
46
46
  var import_lodash = __toESM(require("lodash"));
47
47
  var import_reasoning = require("./common/reasoning");
48
+ const DEEPSEEK_REASONING_EFFORTS = /* @__PURE__ */ new Set(["low", "medium", "high"]);
48
49
  class ReasoningDeepSeek extends import_deepseek.ChatDeepSeek {
49
50
  async _generate(messages, options, runManager) {
50
51
  const reasoningMap = (0, import_reasoning.collectReasoningMap)(messages);
@@ -80,6 +81,7 @@ class DeepSeekProvider extends import_provider.LLMProvider {
80
81
  createModel() {
81
82
  const { apiKey } = this.serviceOptions || {};
82
83
  const { responseFormat } = this.modelOptions || {};
84
+ const reasoningOptions = this.resolveReasoningOptions(this.modelReasoningOptions);
83
85
  const modelKwargs = {};
84
86
  if (responseFormat) {
85
87
  modelKwargs["response_format"] = {
@@ -89,7 +91,11 @@ class DeepSeekProvider extends import_provider.LLMProvider {
89
91
  return new ReasoningDeepSeek({
90
92
  apiKey,
91
93
  ...this.modelOptions,
92
- modelKwargs,
94
+ ...reasoningOptions.modelRequestParams || {},
95
+ modelKwargs: {
96
+ ...modelKwargs,
97
+ ...reasoningOptions.modelKwargs || {}
98
+ },
93
99
  configuration: {
94
100
  baseURL: this.getResolvedBaseURL()
95
101
  },
@@ -124,6 +130,31 @@ class DeepSeekProvider extends import_provider.LLMProvider {
124
130
  }
125
131
  return null;
126
132
  }
133
+ resolveReasoningOptions(reasoning) {
134
+ if (!reasoning || reasoning.mode === "default") {
135
+ return {};
136
+ }
137
+ if (reasoning.mode === "off") {
138
+ return {
139
+ modelKwargs: {
140
+ thinking: {
141
+ type: "disabled"
142
+ }
143
+ }
144
+ };
145
+ }
146
+ if (!DEEPSEEK_REASONING_EFFORTS.has(reasoning.mode)) {
147
+ return {};
148
+ }
149
+ return {
150
+ modelKwargs: {
151
+ thinking: {
152
+ type: "enabled"
153
+ },
154
+ reasoning_effort: reasoning.mode
155
+ }
156
+ };
157
+ }
127
158
  isApiSupportedAttachment(attachment) {
128
159
  return false;
129
160
  }
@@ -132,7 +163,7 @@ const deepseekProviderOptions = {
132
163
  title: "DeepSeek",
133
164
  supportedModel: [import_ai_manager.SupportedModel.LLM],
134
165
  models: {
135
- [import_ai_manager.SupportedModel.LLM]: ["deepseek-chat", "deepseek-reasoner"]
166
+ [import_ai_manager.SupportedModel.LLM]: ["deepseek-v4-pro", "deepseek-v4-flash", "deepseek-chat", "deepseek-reasoner"]
136
167
  },
137
168
  provider: DeepSeekProvider
138
169
  };
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { AIMessageChunk } from '@langchain/core/messages';
10
10
  import { Model } from '@nocobase/database';
11
- import { LLMProvider } from '../provider';
11
+ import { LLMProvider, ReasoningOptions, ResolvedReasoningOptions } from '../provider';
12
12
  import { LLMProviderMeta } from '../../manager/ai-manager';
13
13
  import { CachedDocumentLoader } from '../../document-loader';
14
14
  import { ReasoningChatOpenAI } from '../common/reasoning';
@@ -28,6 +28,7 @@ export declare class KimiProvider extends LLMProvider {
28
28
  status: string;
29
29
  content: string;
30
30
  };
31
+ protected resolveReasoningOptions(reasoning?: ReasoningOptions): ResolvedReasoningOptions;
31
32
  protected isApiSupportedAttachment(attachment: AttachmentModel): boolean;
32
33
  protected get documentLoader(): CachedDocumentLoader;
33
34
  }
@@ -46,6 +46,7 @@ var import_ai_manager = require("../../manager/ai-manager");
46
46
  var import_document_loader = require("../../document-loader");
47
47
  var import_document_loader2 = require("./document-loader");
48
48
  var import_reasoning = require("../common/reasoning");
49
+ const KIMI_THINKING_SWITCH_MODELS = /* @__PURE__ */ new Set(["kimi-k2.5", "kimi-k2.6"]);
49
50
  class KimiProvider extends import_provider.LLMProvider {
50
51
  _documentLoader;
51
52
  get baseURL() {
@@ -55,6 +56,7 @@ class KimiProvider extends import_provider.LLMProvider {
55
56
  const { apiKey } = this.serviceOptions || {};
56
57
  const { responseFormat, structuredOutput } = this.modelOptions || {};
57
58
  const { name, schema } = structuredOutput || {};
59
+ const reasoningOptions = this.resolveReasoningOptions(this.modelReasoningOptions);
58
60
  const responseFormatOptions = {
59
61
  type: responseFormat ?? "text"
60
62
  };
@@ -65,8 +67,10 @@ class KimiProvider extends import_provider.LLMProvider {
65
67
  apiKey,
66
68
  ...this.modelOptions,
67
69
  modelKwargs: {
68
- response_format: responseFormatOptions
70
+ response_format: responseFormatOptions,
71
+ ...reasoningOptions.modelKwargs || {}
69
72
  },
73
+ ...reasoningOptions.modelRequestParams || {},
70
74
  configuration: {
71
75
  baseURL: this.getResolvedBaseURL()
72
76
  }
@@ -100,6 +104,22 @@ class KimiProvider extends import_provider.LLMProvider {
100
104
  }
101
105
  return null;
102
106
  }
107
+ resolveReasoningOptions(reasoning) {
108
+ var _a;
109
+ if (!reasoning || reasoning.mode === "default") {
110
+ return {};
111
+ }
112
+ if (!KIMI_THINKING_SWITCH_MODELS.has((_a = this.modelOptions) == null ? void 0 : _a.model)) {
113
+ return {};
114
+ }
115
+ return {
116
+ modelKwargs: {
117
+ thinking: {
118
+ type: reasoning.mode === "off" ? "disabled" : "enabled"
119
+ }
120
+ }
121
+ };
122
+ }
103
123
  isApiSupportedAttachment(attachment) {
104
124
  var _a;
105
125
  return ((_a = attachment.mimetype) == null ? void 0 : _a.startsWith("image/")) ?? false;
@@ -126,7 +146,7 @@ const kimiProviderOptions = {
126
146
  title: "Kimi",
127
147
  supportedModel: [import_ai_manager.SupportedModel.LLM],
128
148
  models: {
129
- [import_ai_manager.SupportedModel.LLM]: ["kimi-k2.5", "kimi-k2-0905-Preview", "kimi-k2-turbo-preview"]
149
+ [import_ai_manager.SupportedModel.LLM]: ["kimi-k2.7-code", "kimi-k2.7-code-highspeed", "kimi-k2.6", "kimi-k2.5"]
130
150
  },
131
151
  provider: KimiProvider
132
152
  };
@@ -10,21 +10,55 @@ import { EmbeddingsInterface } from '@langchain/core/embeddings';
10
10
  import { AIMessage, AIMessageChunk } from '@langchain/core/messages';
11
11
  import { ChatMistralAI } from '@langchain/mistralai';
12
12
  import { AttachmentModel } from '@nocobase/plugin-file-manager';
13
- import { EmbeddingProvider, LLMProvider, LLMProviderInvokeOptions } from './provider';
13
+ import { EmbeddingProvider, LLMProvider, LLMProviderInvokeOptions, ReasoningMode, ReasoningOptions, ResolvedReasoningOptions } from './provider';
14
14
  import { LLMProviderMeta } from '../manager/ai-manager';
15
15
  import { AIChatContext, AIMessageInput } from '../types/ai-chat-conversation.type';
16
16
  import { Model } from '@nocobase/database';
17
+ declare const MISTRAL_REASONING_MODE_KEY = "__nb_reasoning_mode";
17
18
  type MistralCallOptions = LLMProviderInvokeOptions & {
18
19
  response_format?: {
19
20
  type: 'text' | 'json_object';
20
21
  };
21
22
  };
22
23
  type MistralBeforeRequestHook = (request: Request) => Request | void | Promise<Request | void>;
24
+ type ReasoningChatMistralAIFields = ConstructorParameters<typeof ChatMistralAI>[0] & {
25
+ [MISTRAL_REASONING_MODE_KEY]?: ReasoningMode;
26
+ };
23
27
  export declare const injectMistralReasoningEffort: MistralBeforeRequestHook;
28
+ declare class ReasoningChatMistralAI extends ChatMistralAI {
29
+ private readonly reasoningMode?;
30
+ constructor(fields: ReasoningChatMistralAIFields);
31
+ invocationParams(options?: MistralCallOptions): Omit<import("@mistralai/mistralai/models/components/chatcompletionrequest").ChatCompletionRequest | import("@mistralai/mistralai/models/components/chatcompletionstreamrequest").ChatCompletionStreamRequest, "messages"> | {
32
+ __nb_reasoning_mode: "off" | "minimal" | "low" | "medium" | "high" | "xhigh";
33
+ model: string;
34
+ tools?: import("@mistralai/mistralai/models/components/tool").Tool[];
35
+ metadata?: {
36
+ [k: string]: any;
37
+ } | {
38
+ [k: string]: any;
39
+ };
40
+ stream?: boolean;
41
+ responseFormat?: import("@mistralai/mistralai/models/components/responseformat").ResponseFormat;
42
+ temperature?: number;
43
+ maxTokens?: number;
44
+ topP?: number;
45
+ frequencyPenalty?: number;
46
+ presencePenalty?: number;
47
+ n?: number;
48
+ stop?: string | string[];
49
+ toolChoice?: import("@mistralai/mistralai/models/components/toolchoice").ToolChoice | import("@mistralai/mistralai/models/components/toolchoiceenum").ToolChoiceEnum;
50
+ randomSeed?: number;
51
+ prediction?: import("@mistralai/mistralai/models/components/prediction").Prediction;
52
+ parallelToolCalls?: boolean;
53
+ promptMode?: import("@mistralai/mistralai/models/components/mistralpromptmode").MistralPromptMode;
54
+ safePrompt?: boolean;
55
+ };
56
+ }
24
57
  export declare class MistralProvider extends LLMProvider {
25
58
  chatModel: ChatMistralAI;
26
59
  get baseURL(): string;
27
- createModel(): ChatMistralAI<import("@langchain/mistralai/dist/chat_models").ChatMistralAICallOptions>;
60
+ createModel(): ReasoningChatMistralAI;
61
+ protected resolveReasoningOptions(reasoning?: ReasoningOptions): ResolvedReasoningOptions;
28
62
  stream(context: AIChatContext, options?: MistralCallOptions): Promise<any>;
29
63
  invoke(context: AIChatContext, options?: MistralCallOptions): Promise<any>;
30
64
  listModels(): Promise<{
@@ -39,6 +39,8 @@ var import_ai_manager = require("../manager/ai-manager");
39
39
  const MISTRAL_URL = "https://api.mistral.ai";
40
40
  const MISTRAL_REASONING_EFFORT = "high";
41
41
  const MISTRAL_REASONING_MODELS = /* @__PURE__ */ new Set(["mistral-small-latest", "mistral-medium-3-5"]);
42
+ const MISTRAL_REASONING_EFFORTS = /* @__PURE__ */ new Set(["off", "low", "medium", "high"]);
43
+ const MISTRAL_REASONING_MODE_KEY = "__nb_reasoning_mode";
42
44
  function omitDisabledNumberOptions(options) {
43
45
  for (const key of ["maxTokens", "maxRetries"]) {
44
46
  if (options[key] === -1) {
@@ -73,19 +75,62 @@ const injectMistralReasoningEffort = async (request) => {
73
75
  if (!body || typeof body !== "object") {
74
76
  return;
75
77
  }
78
+ const reasoningMode = body[MISTRAL_REASONING_MODE_KEY];
79
+ delete body[MISTRAL_REASONING_MODE_KEY];
80
+ const shouldRewriteBody = reasoningMode != null;
76
81
  if (typeof body.model !== "string" || !MISTRAL_REASONING_MODELS.has(body.model)) {
82
+ if (shouldRewriteBody) {
83
+ const headers2 = new Headers(request.headers);
84
+ headers2.delete("content-length");
85
+ return new Request(request, {
86
+ headers: headers2,
87
+ body: JSON.stringify(body)
88
+ });
89
+ }
77
90
  return;
78
91
  }
92
+ if (reasoningMode === "off") {
93
+ delete body.reasoning_effort;
94
+ const headers2 = new Headers(request.headers);
95
+ headers2.delete("content-length");
96
+ return new Request(request, {
97
+ headers: headers2,
98
+ body: JSON.stringify(body)
99
+ });
100
+ }
101
+ if (body.reasoning_effort && !shouldRewriteBody) {
102
+ return;
103
+ }
104
+ const reasoningEffort = reasoningMode && reasoningMode !== "default" ? reasoningMode : MISTRAL_REASONING_EFFORT;
79
105
  const headers = new Headers(request.headers);
80
106
  headers.delete("content-length");
81
107
  return new Request(request, {
82
108
  headers,
83
109
  body: JSON.stringify({
84
110
  ...body,
85
- reasoning_effort: MISTRAL_REASONING_EFFORT
111
+ reasoning_effort: reasoningEffort
86
112
  })
87
113
  });
88
114
  };
115
+ class ReasoningChatMistralAI extends import_mistralai.ChatMistralAI {
116
+ reasoningMode;
117
+ constructor(fields) {
118
+ const { [MISTRAL_REASONING_MODE_KEY]: reasoningMode, ...modelFields } = fields;
119
+ super(modelFields);
120
+ this.reasoningMode = reasoningMode;
121
+ }
122
+ invocationParams(options) {
123
+ const params = super.invocationParams(options);
124
+ const mode = this.reasoningMode;
125
+ if (!mode || mode === "default") {
126
+ return params;
127
+ }
128
+ return {
129
+ ...params,
130
+ [MISTRAL_REASONING_MODE_KEY]: mode
131
+ };
132
+ }
133
+ }
89
134
  class MistralProvider extends import_provider.LLMProvider {
90
135
  get baseURL() {
91
136
  return MISTRAL_URL;
@@ -95,14 +140,29 @@ class MistralProvider extends import_provider.LLMProvider {
95
140
  const { responseFormat, structuredOutput, ...modelOptions } = this.modelOptions || {};
96
141
  omitDisabledNumberOptions(modelOptions);
97
142
  const beforeRequestHooks = Array.isArray(modelOptions.beforeRequestHooks) ? [injectMistralReasoningEffort, ...modelOptions.beforeRequestHooks] : [injectMistralReasoningEffort];
98
- return new import_mistralai.ChatMistralAI({
143
+ const reasoningOptions = this.resolveReasoningOptions(this.modelReasoningOptions);
144
+ return new ReasoningChatMistralAI({
99
145
  apiKey,
100
146
  ...modelOptions,
147
+ ...reasoningOptions.modelRequestParams || {},
101
148
  beforeRequestHooks,
102
149
  serverURL: this.getResolvedServerURL(),
103
150
  verbose: false
104
151
  });
105
152
  }
153
+ resolveReasoningOptions(reasoning) {
154
+ if (!reasoning || reasoning.mode === "default") {
155
+ return {};
156
+ }
157
+ if (!MISTRAL_REASONING_EFFORTS.has(reasoning.mode)) {
158
+ return {};
159
+ }
160
+ return {
161
+ modelRequestParams: {
162
+ [MISTRAL_REASONING_MODE_KEY]: reasoning.mode
163
+ }
164
+ };
165
+ }
106
166
  async stream(context, options) {
107
167
  return super.stream(context, this.withResponseFormat(options));
108
168
  }
@@ -7,9 +7,10 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { ChatOpenAI } from '@langchain/openai';
10
- import { LLMProvider } from '../provider';
10
+ import { LLMProvider, ReasoningOptions, ResolvedReasoningOptions } from '../provider';
11
11
  export declare class OpenAICompletionsProvider extends LLMProvider {
12
12
  chatModel: ChatOpenAI;
13
13
  get baseURL(): string;
14
14
  createModel(): ChatOpenAI<import("@langchain/openai/dist/chat_models/index.cjs").ChatOpenAICallOptions>;
15
+ protected resolveReasoningOptions(reasoning?: ReasoningOptions): ResolvedReasoningOptions;
15
16
  }
@@ -39,6 +39,7 @@ class OpenAICompletionsProvider extends import_provider.LLMProvider {
39
39
  const { apiKey } = this.serviceOptions || {};
40
40
  const { responseFormat, structuredOutput } = this.modelOptions || {};
41
41
  const { name, schema } = structuredOutput || {};
42
+ const reasoningOptions = this.resolveReasoningOptions(this.modelReasoningOptions);
42
43
  const responseFormatOptions = {
43
44
  type: responseFormat ?? "text"
44
45
  };
@@ -48,14 +49,29 @@ class OpenAICompletionsProvider extends import_provider.LLMProvider {
48
49
  return new import_openai.ChatOpenAI({
49
50
  apiKey,
50
51
  ...this.modelOptions,
52
+ ...reasoningOptions.modelRequestParams || {},
51
53
  modelKwargs: {
52
- response_format: responseFormatOptions
54
+ response_format: responseFormatOptions,
55
+ ...reasoningOptions.modelKwargs || {}
53
56
  },
54
57
  configuration: {
55
58
  baseURL: this.getResolvedBaseURL()
56
59
  }
57
60
  });
58
61
  }
62
+ resolveReasoningOptions(reasoning) {
63
+ if (!reasoning || reasoning.mode === "default") {
64
+ return {};
65
+ }
66
+ const effort = reasoning.mode === "off" ? "none" : reasoning.mode;
67
+ return {
68
+ modelRequestParams: {
69
+ reasoning: {
70
+ effort
71
+ }
72
+ }
73
+ };
74
+ }
59
75
  }
60
76
  // Annotate the CommonJS export names for ESM import in node:
61
77
  0 && (module.exports = {
@@ -7,7 +7,7 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
  import { ChatOpenAI } from '@langchain/openai';
10
- import { LLMProvider } from '../provider';
10
+ import { LLMProvider, ReasoningOptions, ResolvedReasoningOptions } from '../provider';
11
11
  import { Model } from '@nocobase/database';
12
12
  import { AIMessageChunk } from '@langchain/core/messages';
13
13
  export declare class OpenAIResponsesProvider extends LLMProvider {
@@ -22,6 +22,7 @@ export declare class OpenAIResponsesProvider extends LLMProvider {
22
22
  role: any;
23
23
  };
24
24
  protected builtInTools(): any[];
25
+ protected resolveReasoningOptions(reasoning?: ReasoningOptions): ResolvedReasoningOptions;
25
26
  isToolConflict(): boolean;
26
27
  parseWebSearchAction(chunk: AIMessageChunk): {
27
28
  type: string;
@@ -40,6 +40,7 @@ class OpenAIResponsesProvider extends import_provider.LLMProvider {
40
40
  const { apiKey } = this.serviceOptions || {};
41
41
  const { responseFormat, structuredOutput } = this.modelOptions || {};
42
42
  const { name, schema, strict } = structuredOutput || {};
43
+ const reasoningOptions = this.resolveReasoningOptions(this.modelReasoningOptions);
43
44
  let responseFormatOptions = {
44
45
  type: responseFormat ?? "text"
45
46
  };
@@ -54,10 +55,12 @@ class OpenAIResponsesProvider extends import_provider.LLMProvider {
54
55
  return new import_openai.ChatOpenAI({
55
56
  apiKey,
56
57
  ...this.modelOptions,
58
+ ...reasoningOptions.modelRequestParams || {},
57
59
  modelKwargs: {
58
60
  text: {
59
61
  format: responseFormatOptions
60
- }
62
+ },
63
+ ...reasoningOptions.modelKwargs || {}
61
64
  },
62
65
  configuration: {
63
66
  baseURL: this.getResolvedBaseURL()
@@ -130,6 +133,19 @@ class OpenAIResponsesProvider extends import_provider.LLMProvider {
130
133
  }
131
134
  return [];
132
135
  }
136
+ resolveReasoningOptions(reasoning) {
137
+ if (!reasoning || reasoning.mode === "default") {
138
+ return {};
139
+ }
140
+ const effort = reasoning.mode === "off" ? "none" : reasoning.mode;
141
+ return {
142
+ modelRequestParams: {
143
+ reasoning: {
144
+ effort
145
+ }
146
+ }
147
+ };
148
+ }
133
149
  isToolConflict() {
134
150
  return false;
135
151
  }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+ import { LLMProvider } from './provider';
10
+ import { LLMProviderMeta } from '../manager/ai-manager';
11
+ import { AIMessageChunk, BaseMessageChunk } from '@langchain/core/messages';
12
+ import { ReasoningChatOpenAI } from './common/reasoning';
13
+ import { AttachmentModel } from '@nocobase/plugin-file-manager';
14
+ import { Model } from '@nocobase/database';
15
+ export declare class OrcaRouterProvider extends LLMProvider {
16
+ chatModel: ReasoningChatOpenAI;
17
+ get baseURL(): string;
18
+ createModel(): ChatOrcaRouterCompletions;
19
+ protected isApiSupportedAttachment(attachment: AttachmentModel): boolean;
20
+ parseResponseMessage(message: Model): {
21
+ key: any;
22
+ createdAt: any;
23
+ content: any;
24
+ role: any;
25
+ };
26
+ parseReasoningContent(chunk: AIMessageChunk): {
27
+ status: string;
28
+ content: string;
29
+ };
30
+ }
31
+ declare class ChatOrcaRouterCompletions extends ReasoningChatOpenAI {
32
+ _convertCompletionsDeltaToBaseMessageChunk(delta: any, rawResponse: any, defaultRole: any): BaseMessageChunk;
33
+ }
34
+ export declare const orcarouterProviderOptions: LLMProviderMeta;
35
+ export {};
@@ -0,0 +1,155 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __create = Object.create;
11
+ var __defProp = Object.defineProperty;
12
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
13
+ var __getOwnPropNames = Object.getOwnPropertyNames;
14
+ var __getProtoOf = Object.getPrototypeOf;
15
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
16
+ var __export = (target, all) => {
17
+ for (var name in all)
18
+ __defProp(target, name, { get: all[name], enumerable: true });
19
+ };
20
+ var __copyProps = (to, from, except, desc) => {
21
+ if (from && typeof from === "object" || typeof from === "function") {
22
+ for (let key of __getOwnPropNames(from))
23
+ if (!__hasOwnProp.call(to, key) && key !== except)
24
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
25
+ }
26
+ return to;
27
+ };
28
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
29
+ // If the importer is in node compatibility mode or this is not an ESM
30
+ // file that has been converted to a CommonJS file using a Babel-
31
+ // compatible transform (i.e. "__esModule" has not been set), then set
32
+ // "default" to the CommonJS "module.exports" for node compatibility.
33
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
34
+ mod
35
+ ));
36
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
37
+ var orcarouter_exports = {};
38
+ __export(orcarouter_exports, {
39
+ OrcaRouterProvider: () => OrcaRouterProvider,
40
+ orcarouterProviderOptions: () => orcarouterProviderOptions
41
+ });
42
+ module.exports = __toCommonJS(orcarouter_exports);
43
+ var import_provider = require("./provider");
44
+ var import_ai_manager = require("../manager/ai-manager");
45
+ var import_lodash = __toESM(require("lodash"));
46
+ var import_openai = require("@langchain/openai");
47
+ var import_messages = require("@langchain/core/messages");
48
+ var import_reasoning = require("./common/reasoning");
49
+ class OrcaRouterProvider extends import_provider.LLMProvider {
50
+ get baseURL() {
51
+ return "https://api.orcarouter.ai/v1";
52
+ }
53
+ createModel() {
54
+ const { apiKey, httpReferer, xTitle } = this.serviceOptions || {};
55
+ const { responseFormat, structuredOutput } = this.modelOptions || {};
56
+ const { name, schema } = structuredOutput || {};
57
+ const responseFormatOptions = {
58
+ type: responseFormat ?? "text"
59
+ };
60
+ if (responseFormat === "json_schema" && schema) {
61
+ responseFormatOptions["json_schema"] = { schema, name: name ?? "schema" };
62
+ }
63
+ const defaultHeaders = {};
64
+ if (httpReferer) {
65
+ defaultHeaders["HTTP-Referer"] = httpReferer;
66
+ }
67
+ if (xTitle) {
68
+ defaultHeaders["X-Title"] = xTitle;
69
+ }
70
+ return new ChatOrcaRouterCompletions({
71
+ apiKey,
72
+ ...this.modelOptions,
73
+ modelKwargs: {
74
+ response_format: responseFormatOptions
75
+ },
76
+ configuration: {
77
+ baseURL: this.getResolvedBaseURL(),
78
+ ...Object.keys(defaultHeaders).length ? { defaultHeaders } : {}
79
+ }
80
+ });
81
+ }
82
+ isApiSupportedAttachment(attachment) {
83
+ const media = ["image/"];
84
+ return media.some((it) => {
85
+ var _a;
86
+ return (_a = attachment == null ? void 0 : attachment.mimetype) == null ? void 0 : _a.startsWith(it);
87
+ });
88
+ }
89
+ parseResponseMessage(message) {
90
+ var _a;
91
+ const result = super.parseResponseMessage(message);
92
+ if (["user", "tool"].includes(result == null ? void 0 : result.role)) {
93
+ return result;
94
+ }
95
+ const { metadata } = (message == null ? void 0 : message.toJSON()) ?? {};
96
+ if (!import_lodash.default.isEmpty((_a = metadata == null ? void 0 : metadata.additional_kwargs) == null ? void 0 : _a.reasoning_content)) {
97
+ result.content = {
98
+ ...result.content ?? {},
99
+ reasoning: {
100
+ status: "stop",
101
+ content: metadata == null ? void 0 : metadata.additional_kwargs.reasoning_content
102
+ }
103
+ };
104
+ }
105
+ return result;
106
+ }
107
+ parseReasoningContent(chunk) {
108
+ var _a;
109
+ if (!import_lodash.default.isEmpty((_a = chunk == null ? void 0 : chunk.additional_kwargs) == null ? void 0 : _a.reasoning_content)) {
110
+ return {
111
+ status: "streaming",
112
+ content: chunk.additional_kwargs.reasoning_content
113
+ };
114
+ }
115
+ return null;
116
+ }
117
+ }
118
+ class ChatOrcaRouterCompletions extends import_reasoning.ReasoningChatOpenAI {
119
+ _convertCompletionsDeltaToBaseMessageChunk(delta, rawResponse, defaultRole) {
120
+ const chunk = (0, import_openai.convertCompletionsDeltaToBaseMessageChunk)({
121
+ delta,
122
+ rawResponse,
123
+ includeRawResponse: this.__includeRawResponse,
124
+ defaultRole
125
+ });
126
+ if (chunk instanceof import_messages.AIMessageChunk) {
127
+ if (delta.reasoning_content) {
128
+ chunk.additional_kwargs.reasoning_content = delta.reasoning_content;
129
+ }
130
+ }
131
+ return chunk;
132
+ }
133
+ }
134
+ const orcarouterProviderOptions = {
135
+ title: "OrcaRouter",
136
+ supportedModel: [import_ai_manager.SupportedModel.LLM],
137
+ models: {
138
+ [import_ai_manager.SupportedModel.LLM]: [
139
+ "orcarouter/auto",
140
+ "openai/gpt-5.5",
141
+ "google/gemini-3.5-flash",
142
+ "anthropic/claude-opus-4.8",
143
+ "grok/grok-4.3",
144
+ "deepseek/deepseek-v4-pro",
145
+ "minimax/minimax-m2.7",
146
+ "qwen/qwen3.7-max"
147
+ ]
148
+ },
149
+ provider: OrcaRouterProvider
150
+ };
151
+ // Annotate the CommonJS export names for ESM import in node:
152
+ 0 && (module.exports = {
153
+ OrcaRouterProvider,
154
+ orcarouterProviderOptions
155
+ });
@@ -27,6 +27,11 @@ export type LLMProviderInvokeOptions = {
27
27
  modelRequestParams?: Record<string, any>;
28
28
  [key: string]: any;
29
29
  };
30
+ export type ReasoningMode = 'default' | 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh';
31
+ export type ReasoningOptions = {
32
+ mode: ReasoningMode;
33
+ };
34
+ export type ResolvedReasoningOptions = Pick<LLMProviderInvokeOptions, 'modelKwargs' | 'modelRequestParams'>;
30
35
  export type LLMModelRequestBuilderResult = {
31
36
  context: AIChatContext;
32
37
  options?: LLMProviderInvokeOptions;
@@ -45,10 +50,12 @@ export declare abstract class LLMProvider {
45
50
  serviceOptions: Record<string, any>;
46
51
  modelOptions: Record<string, any> | undefined;
47
52
  chatModel: any;
53
+ protected modelReasoningOptions: ReasoningOptions | undefined;
48
54
  abstract createModel(): BaseChatModel | any;
49
55
  get baseURL(): string | null;
50
56
  constructor(opts: LLMProviderOptions);
51
57
  protected getModelRequestBuilder(_model?: string): LLMModelRequestBuilder | null;
58
+ protected resolveReasoningOptions(_reasoning?: ReasoningOptions): ResolvedReasoningOptions;
52
59
  prepareChain(context: AIChatContext): any;
53
60
  invoke(context: AIChatContext, options?: LLMProviderInvokeOptions): Promise<any>;
54
61
  stream(context: AIChatContext, options?: any): Promise<any>;
@@ -84,6 +84,7 @@ class LLMProvider {
84
84
  serviceOptions;
85
85
  modelOptions;
86
86
  chatModel;
87
+ modelReasoningOptions;
87
88
  get baseURL() {
88
89
  return null;
89
90
  }
@@ -92,13 +93,18 @@ class LLMProvider {
92
93
  this.app = app;
93
94
  this.serviceOptions = resolveServiceOptions(serviceOptions, app);
94
95
  if (modelOptions) {
95
- this.modelOptions = modelOptions;
96
+ const { _reasoning, ...restModelOptions } = modelOptions;
97
+ this.modelReasoningOptions = _reasoning;
98
+ this.modelOptions = restModelOptions;
96
99
  this.chatModel = this.createModel();
97
100
  }
98
101
  }
99
102
  getModelRequestBuilder(_model) {
100
103
  return null;
101
104
  }
105
+ resolveReasoningOptions(_reasoning) {
106
+ return {};
107
+ }
102
108
  prepareChain(context) {
103
109
  var _a, _b, _c, _d;
104
110
  let chain = this.chatModel;