@shenghuabi/openai 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.mjs ADDED
@@ -0,0 +1,1015 @@
1
+ // packages/openai/chat/service.ts
2
+ import { inject as inject2 } from "static-injector";
3
+
4
+ // packages/openai/chat/vendor/openai.ts
5
+ import { OpenAI } from "openai";
6
+
7
+ // packages/openai/chat/message.define.ts
8
+ import * as v from "valibot";
9
+ var ChatCompletionContentPartStr = v.object({
10
+ text: v.string(),
11
+ type: v.optional(v.literal("text"), "text")
12
+ });
13
+ var ChatCompletionContentPartImage = v.object({
14
+ image_url: v.object({
15
+ url: v.string(),
16
+ detail: v.optional(v.picklist(["auto", "low", "high"]))
17
+ }),
18
+ type: v.optional(v.literal("image_url"), "image_url")
19
+ });
20
+ var ChatCompletionContentPart = v.array(
21
+ v.union([ChatCompletionContentPartStr, ChatCompletionContentPartImage])
22
+ );
23
+ var SystemChatCompletionContent = v.array(
24
+ ChatCompletionContentPartStr
25
+ );
26
+ var UserChatCompletionContent = ChatCompletionContentPart;
27
+ var AssistantChatCompletionContent = v.array(
28
+ ChatCompletionContentPartStr
29
+ );
30
+ var SystemChatMessage = v.object({
31
+ role: v.optional(v.literal("system"), "system"),
32
+ content: SystemChatCompletionContent
33
+ });
34
+ var UserChatMessage = v.object({
35
+ role: v.optional(v.literal("user"), "user"),
36
+ content: UserChatCompletionContent
37
+ });
38
+ var AssistantChatMessage = v.object({
39
+ role: v.optional(v.literal("assistant"), "assistant"),
40
+ content: AssistantChatCompletionContent,
41
+ thinkContent: v.optional(v.string())
42
+ });
43
+ var ChatMessageItemDefine = v.union([
44
+ SystemChatMessage,
45
+ UserChatMessage,
46
+ AssistantChatMessage
47
+ ]);
48
+ var ChatMessageListDefine = v.array(ChatMessageItemDefine);
49
+
50
+ // packages/openai/chat/vendor/openai.ts
51
+ import * as v2 from "valibot";
52
+ var OpenAIChat = class {
53
+ constructor(options) {
54
+ this.options = options;
55
+ }
56
+ options;
57
+ instance;
58
+ extraBody = {};
59
+ extraHeaders = {};
60
+ init() {
61
+ this.instance = new OpenAI({
62
+ apiKey: this.options.apiKey || " ",
63
+ baseURL: this.options.baseURL ?? void 0
64
+ });
65
+ }
66
+ async #createStream(input, options) {
67
+ return await this.instance.chat.completions.create(input, { headers: this.extraHeaders, ...options }).catch(async (error) => {
68
+ if (options?.tryPull?.(error)) {
69
+ await options?.pullModel(this.options.model);
70
+ return this.#createStream(input, {
71
+ ...options,
72
+ tryPull: () => false
73
+ });
74
+ }
75
+ throw error;
76
+ });
77
+ }
78
+ async *stream(input, options) {
79
+ const input2 = {
80
+ ...input,
81
+ messages: v2.parse(ChatMessageListDefine, input.messages),
82
+ model: this.options.model,
83
+ stream: true,
84
+ max_tokens: this.options.max_tokens,
85
+ top_p: this.options.top_p,
86
+ temperature: this.options.temperature,
87
+ frequency_penalty: this.options.frequency_penalty,
88
+ presence_penalty: this.options.presence_penalty,
89
+ seed: this.options.seed,
90
+ stop: this.options.stop,
91
+ ...this.extraBody
92
+ };
93
+ const result = await this.#createStream(input2, options);
94
+ let isThinking = 0;
95
+ for await (const item of result) {
96
+ try {
97
+ if (!item.choices[0].finish_reason) {
98
+ if (typeof item.choices[0].delta.reasoning_content === "string" || typeof item.choices[0].delta.reasoning === "string") {
99
+ if (isThinking === 0) {
100
+ isThinking = 1;
101
+ yield "<thinking>";
102
+ }
103
+ const result2 = item.choices[0].delta.reasoning_content ?? item.choices[0].delta.reasoning;
104
+ if (result2) {
105
+ yield result2;
106
+ }
107
+ } else if (typeof item.choices[0].delta.content === "string") {
108
+ if (isThinking === 1) {
109
+ yield "</thinking>";
110
+ isThinking = 2;
111
+ }
112
+ const result2 = item.choices[0].delta.content;
113
+ if (result2) {
114
+ yield result2;
115
+ }
116
+ }
117
+ }
118
+ } catch (error) {
119
+ throw error;
120
+ }
121
+ }
122
+ }
123
+ // 工具调用,暂时不清楚怎么调用...是返回工具,还是返回对话
124
+ async callTool(input, options) {
125
+ const result = await this.instance.chat.completions.create(
126
+ {
127
+ ...input,
128
+ messages: v2.parse(ChatMessageListDefine, input.messages),
129
+ model: this.options.model,
130
+ max_tokens: this.options.max_tokens,
131
+ top_p: this.options.top_p,
132
+ temperature: this.options.temperature,
133
+ frequency_penalty: this.options.frequency_penalty,
134
+ presence_penalty: this.options.presence_penalty,
135
+ seed: this.options.seed,
136
+ stop: this.options.stop,
137
+ ...this.extraBody,
138
+ stream: false
139
+ },
140
+ { headers: this.extraHeaders, ...options }
141
+ );
142
+ const isTool = !!result.choices[0].message.tool_calls;
143
+ if (isTool) {
144
+ const data = result.choices[0].message.tool_calls[0].function;
145
+ return {
146
+ type: "function",
147
+ function: {
148
+ name: data.name,
149
+ arguments: JSON.parse(data.arguments)
150
+ }
151
+ };
152
+ }
153
+ return { type: "text", text: result.choices[0].message.content };
154
+ }
155
+ };
156
+
157
+ // packages/openai/chat/vendor/openai-compatible.factory.ts
158
+ var VendorObject = {
159
+ "360": { options: { baseURL: "https://ai.360.cn", modelList: [] } },
160
+ azure: { options: { baseURL: "", modelList: [] } },
161
+ moonshot: { options: { baseURL: "https://api.moonshot.cn", modelList: [] } },
162
+ baichuan: {
163
+ options: { baseURL: "https://api.baichuan-ai.com", modelList: [] }
164
+ },
165
+ minimax: { options: { baseURL: "https://api.minimax.chat", modelList: [] } },
166
+ mistralai: { options: { baseURL: "https://api.mistral.ai", modelList: [] } },
167
+ groq: {
168
+ options: { baseURL: "https://api.groq.com/openai", modelList: [] }
169
+ },
170
+ lingyiwanwu: {
171
+ options: { baseURL: "https://api.lingyiwanwu.com", modelList: [] }
172
+ },
173
+ stepfun: { options: { baseURL: "https://api.stepfun.com", modelList: [] } },
174
+ deepseek: { options: { baseURL: "https://api.deepseek.com", modelList: [] } },
175
+ "together.ai": {
176
+ options: { baseURL: "https://api.together.xyz", modelList: [] }
177
+ },
178
+ volcengine: {
179
+ options: {
180
+ baseURL: "https://ark.cn-beijing.volces.com/api/v3",
181
+ modelList: []
182
+ }
183
+ },
184
+ novita: {
185
+ options: { baseURL: "https://api.novita.ai/v3/openai", modelList: [] }
186
+ },
187
+ siliconflow: {
188
+ options: { baseURL: "https://api.siliconflow.cn", modelList: [] }
189
+ },
190
+ // 官网明确兼容
191
+ // https://help.aliyun.com/zh/model-studio/developer-reference/use-qwen-by-calling-api?spm=a2c6h.13066369.question.5.3a0e527bbxeJLJ#4ec3e641c294d
192
+ tongyi: {
193
+ options: {
194
+ baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
195
+ modelList: []
196
+ }
197
+ },
198
+ // 文档上看是一样的
199
+ // https://open.bigmodel.cn/dev/api/normal-model/glm-4
200
+ zhipu: {
201
+ options: {
202
+ baseURL: "https://open.bigmodel.cn/api/paas/v4",
203
+ modelList: []
204
+ }
205
+ },
206
+ // 明确兼容
207
+ // https://www.xfyun.cn/doc/spark/HTTP%E8%B0%83%E7%94%A8%E6%96%87%E6%A1%A3.html#_7-%E4%BD%BF%E7%94%A8openai-sdk%E8%AF%B7%E6%B1%82%E7%A4%BA%E4%BE%8B
208
+ spark: {
209
+ options: {
210
+ baseURL: "https://spark-api-open.xf-yun.com/v1",
211
+ modelList: []
212
+ }
213
+ },
214
+ // 官方兼容
215
+ // https://cloud.tencent.com/document/product/1729/111007
216
+ hunyuan: {
217
+ options: {
218
+ baseURL: "https://api.hunyuan.cloud.tencent.com/v1",
219
+ modelList: []
220
+ }
221
+ }
222
+ };
223
+ function createCompatibleFactory(options) {
224
+ const vendorConfig = VendorObject[options.vendor];
225
+ return new OpenAIChat({
226
+ ...options,
227
+ baseURL: vendorConfig.options.baseURL
228
+ });
229
+ }
230
+
231
+ // packages/openai/chat/vendor/genmini/genmini.ts
232
+ import { GoogleGenerativeAI } from "@google/generative-ai";
233
+
234
+ // packages/openai/chat/vendor/genmini/message.define.ts
235
+ import * as v3 from "valibot";
236
+ import { isTruthy } from "@cyia/util";
237
+ function messageContentTransform(list) {
238
+ return list.map((item) => item.type === "text" ? { text: item.text } : void 0).filter(isTruthy);
239
+ }
240
+ var GenminiSystemMessageDefine = v3.pipe(
241
+ SystemChatMessage,
242
+ v3.transform((item) => {
243
+ return {
244
+ role: "system",
245
+ parts: messageContentTransform(item.content)
246
+ };
247
+ })
248
+ );
249
+ var GenminiChatMessageListDefine = v3.pipe(
250
+ v3.array(
251
+ v3.union([
252
+ v3.pipe(
253
+ UserChatMessage,
254
+ v3.transform((item) => {
255
+ return {
256
+ role: "user",
257
+ parts: messageContentTransform(item.content)
258
+ };
259
+ })
260
+ ),
261
+ v3.pipe(
262
+ AssistantChatMessage,
263
+ v3.transform((item) => {
264
+ return {
265
+ role: "model",
266
+ parts: messageContentTransform(item.content)
267
+ };
268
+ })
269
+ )
270
+ ])
271
+ )
272
+ );
273
+
274
+ // packages/openai/chat/vendor/genmini/genmini.ts
275
+ import * as v4 from "valibot";
276
+ var GeminiChat = class extends OpenAIChat {
277
+ #ggai;
278
+ init() {
279
+ this.#ggai = new GoogleGenerativeAI(this.options.apiKey);
280
+ }
281
+ async *stream(input, options) {
282
+ const model = this.#ggai.getGenerativeModel({
283
+ model: this.options.model,
284
+ generationConfig: {
285
+ topP: this.options.top_p,
286
+ temperature: this.options.temperature,
287
+ maxOutputTokens: this.options.max_tokens
288
+ }
289
+ });
290
+ const messages = input.messages;
291
+ const data = {};
292
+ if (messages[0].role === "system") {
293
+ const system = messages.shift();
294
+ data.systemInstruction = v4.parse(GenminiSystemMessageDefine, system);
295
+ }
296
+ data.history = v4.parse(GenminiChatMessageListDefine, messages);
297
+ const lastMessage = data.history.pop();
298
+ const chat = model.startChat(data);
299
+ const result = await chat.sendMessageStream(lastMessage.parts, {
300
+ signal: options?.signal
301
+ });
302
+ for await (const chunk of result.stream) {
303
+ const chunkText = chunk.text();
304
+ yield chunkText;
305
+ }
306
+ }
307
+ };
308
+
309
+ // packages/openai/chat/vendor/anthropic/anthropic.ts
310
+ import Anthropic from "@anthropic-ai/sdk";
311
+
312
+ // packages/openai/chat/vendor/anthropic/message.define.ts
313
+ import { isTruthy as isTruthy2 } from "@cyia/util";
314
+ import * as v5 from "valibot";
315
+ function baseImageSplit(str) {
316
+ const mimeTypeIndex = str.indexOf(";");
317
+ const dataStartIndex = str.indexOf(",", mimeTypeIndex);
318
+ return {
319
+ mimeType: str.slice(5, mimeTypeIndex),
320
+ data: str.slice(dataStartIndex + 1)
321
+ };
322
+ }
323
+ var AnthropicSystemMessageDefine = v5.pipe(
324
+ SystemChatCompletionContent,
325
+ v5.transform((list) => {
326
+ return list.map((item) => item.type === "text" ? item : void 0).filter(isTruthy2);
327
+ })
328
+ );
329
+ function contentConvert(part) {
330
+ if (part.type === "text") {
331
+ return part;
332
+ }
333
+ const result = baseImageSplit(part.image_url.url);
334
+ return {
335
+ type: "image",
336
+ source: {
337
+ type: "base64",
338
+ media_type: result.mimeType,
339
+ data: result.data
340
+ }
341
+ };
342
+ }
343
+ var UserMessageDefine = v5.pipe(
344
+ UserChatMessage,
345
+ v5.transform((item) => {
346
+ return {
347
+ ...item,
348
+ content: item.content.map(contentConvert)
349
+ };
350
+ })
351
+ );
352
+ var AssistantMessageDefine = v5.pipe(
353
+ AssistantChatMessage,
354
+ v5.transform((item) => {
355
+ return {
356
+ ...item,
357
+ content: item.content.map(contentConvert)
358
+ };
359
+ })
360
+ );
361
+ var AnthropicChatMessageListDefine = v5.array(
362
+ v5.union([UserMessageDefine, AssistantMessageDefine])
363
+ );
364
+
365
+ // packages/openai/chat/vendor/anthropic/anthropic.ts
366
+ import { createAsyncGeneratorAdapter } from "@cyia/util";
367
+ import * as v6 from "valibot";
368
+ var AnthropicChat = class extends OpenAIChat {
369
+ client;
370
+ init() {
371
+ this.client = new Anthropic({ apiKey: this.options.apiKey });
372
+ }
373
+ async *stream(input, options) {
374
+ const body = {
375
+ model: this.options.model,
376
+ max_tokens: this.options.max_tokens,
377
+ top_p: this.options.top_p,
378
+ temperature: this.options.temperature,
379
+ stream: true
380
+ };
381
+ if (input.messages[0].role === "system") {
382
+ const system = input.messages.shift().content;
383
+ body.system = v6.parse(AnthropicSystemMessageDefine, system);
384
+ }
385
+ body.messages = v6.parse(AnthropicChatMessageListDefine, input.messages);
386
+ const result = this.client.messages.stream(body, {
387
+ signal: options?.signal
388
+ });
389
+ const aga = createAsyncGeneratorAdapter();
390
+ result.once("end", () => aga.complete());
391
+ result.on("text", (text) => {
392
+ aga.next(text);
393
+ });
394
+ for await (const item of aga.getData()) {
395
+ yield item;
396
+ }
397
+ }
398
+ async callTool(input, options) {
399
+ const body = {};
400
+ if (input.messages[0].role === "system") {
401
+ const system = input.messages.shift().content;
402
+ body.system = v6.parse(AnthropicSystemMessageDefine, system);
403
+ }
404
+ body.messages = v6.parse(AnthropicChatMessageListDefine, input.messages);
405
+ const result = await this.client.messages.create(
406
+ {
407
+ ...body,
408
+ model: this.options.model,
409
+ max_tokens: this.options.max_tokens,
410
+ top_p: this.options.top_p,
411
+ temperature: this.options.temperature,
412
+ stream: false,
413
+ tools: input.tools.map((item) => {
414
+ return {
415
+ name: item.function.name,
416
+ description: item.function.description,
417
+ input_schema: item.function.parameters
418
+ };
419
+ })
420
+ },
421
+ {
422
+ signal: options?.signal
423
+ }
424
+ );
425
+ const isToolResult = result.content.find(
426
+ (item) => item.type === "tool_use"
427
+ );
428
+ if (isToolResult) {
429
+ return {
430
+ type: "function",
431
+ function: {
432
+ name: isToolResult.name,
433
+ arguments: isToolResult.input
434
+ }
435
+ };
436
+ }
437
+ const text = result.content.find((item) => item.type === "text");
438
+ return {
439
+ type: "text",
440
+ text: text.text
441
+ };
442
+ }
443
+ };
444
+
445
+ // packages/openai/chat/util/create.ts
446
+ function createSystemMessage() {
447
+ return { role: "system" };
448
+ }
449
+ function createUserMessage(text) {
450
+ return {
451
+ role: "user",
452
+ content: typeof text === "string" ? [{ type: "text", text: "" }] : void 0
453
+ };
454
+ }
455
+ function createAssistantMessage(text = "") {
456
+ return {
457
+ role: "assistant",
458
+ content: [{ type: "text", text }]
459
+ };
460
+ }
461
+
462
+ // packages/openai/chat/const.ts
463
+ var ThinkList = [
464
+ "think",
465
+ "thinking",
466
+ "reason",
467
+ "reasoning",
468
+ "thought",
469
+ "Thought"
470
+ ];
471
+
472
+ // packages/openai/chat/options.define.ts
473
+ import {
474
+ asControl,
475
+ componentClass,
476
+ patchInputs,
477
+ patchWrappers,
478
+ setComponent
479
+ } from "@piying/view-angular-core";
480
+ import * as v7 from "valibot";
481
+ import {
482
+ asVirtualGroup,
483
+ metadataPipe,
484
+ omitIntersect
485
+ } from "@piying/valibot-visit";
486
+ var VendorList = [
487
+ {
488
+ value: "openai",
489
+ label: "openai",
490
+ options: {
491
+ baseURL: "",
492
+ modelList: [],
493
+ description: "如果是openai兼容的厂商,需要设置baseURL"
494
+ }
495
+ },
496
+ {
497
+ value: "360",
498
+ label: "360",
499
+ options: { baseURL: "https://ai.360.cn", modelList: [] }
500
+ },
501
+ {
502
+ value: "azure",
503
+ label: "azure",
504
+ options: { baseURL: "", modelList: [] }
505
+ },
506
+ {
507
+ value: "moonshot",
508
+ label: "moonshot",
509
+ options: { baseURL: "https://api.moonshot.cn", modelList: [] }
510
+ },
511
+ {
512
+ value: "baichuan",
513
+ label: "baichuan",
514
+ options: { baseURL: "https://api.baichuan-ai.com", modelList: [] }
515
+ },
516
+ {
517
+ value: "minimax",
518
+ label: "minimax",
519
+ options: { baseURL: "https://api.minimax.chat", modelList: [] }
520
+ },
521
+ {
522
+ value: "mistralai",
523
+ label: "mistralai",
524
+ options: { baseURL: "https://api.mistral.ai", modelList: [] }
525
+ },
526
+ {
527
+ value: "groq",
528
+ label: "groq",
529
+ options: { baseURL: "https://api.groq.com/openai", modelList: [] }
530
+ },
531
+ {
532
+ value: "lingyiwanwu",
533
+ label: "lingyiwanwu",
534
+ options: { baseURL: "https://api.lingyiwanwu.com", modelList: [] }
535
+ },
536
+ {
537
+ value: "stepfun",
538
+ label: "stepfun",
539
+ options: { baseURL: "https://api.stepfun.com", modelList: [] }
540
+ },
541
+ {
542
+ value: "deepseek",
543
+ label: "deepseek",
544
+ options: { baseURL: "https://api.deepseek.com", modelList: [] }
545
+ },
546
+ {
547
+ value: "together.ai",
548
+ label: "together.ai",
549
+ options: { baseURL: "https://api.together.xyz", modelList: [] }
550
+ },
551
+ {
552
+ value: "volcengine",
553
+ label: "volcengine",
554
+ options: {
555
+ baseURL: "https://ark.cn-beijing.volces.com/api/v3",
556
+ modelList: []
557
+ }
558
+ },
559
+ {
560
+ value: "novita",
561
+ label: "novita",
562
+ options: { baseURL: "https://api.novita.ai/v3/openai", modelList: [] }
563
+ },
564
+ {
565
+ value: "siliconflow",
566
+ label: "siliconflow",
567
+ options: { baseURL: "https://api.siliconflow.cn", modelList: [] }
568
+ },
569
+ {
570
+ value: "tongyi",
571
+ label: "tongyi",
572
+ options: {
573
+ baseURL: "https://dashscope.aliyuncs.com/compatible-mode/v1",
574
+ modelList: []
575
+ }
576
+ },
577
+ {
578
+ value: "zhipu",
579
+ label: "zhipu",
580
+ options: {
581
+ baseURL: "https://open.bigmodel.cn/api/paas/v4",
582
+ modelList: []
583
+ }
584
+ },
585
+ {
586
+ value: "spark",
587
+ label: "spark",
588
+ options: {
589
+ baseURL: "https://spark-api-open.xf-yun.com/v1",
590
+ modelList: []
591
+ }
592
+ },
593
+ {
594
+ value: "hunyuan",
595
+ label: "hunyuan",
596
+ options: {
597
+ baseURL: "https://api.hunyuan.cloud.tencent.com/v1",
598
+ modelList: []
599
+ }
600
+ },
601
+ {
602
+ value: "gemini",
603
+ label: "gemini",
604
+ options: {
605
+ baseURL: "",
606
+ modelList: [],
607
+ description: "非openai兼容,手动适配,如果有问题欢迎报告"
608
+ }
609
+ },
610
+ {
611
+ value: "claude",
612
+ label: "claude",
613
+ options: {
614
+ baseURL: "",
615
+ modelList: [],
616
+ description: "非openai兼容,手动适配,如果有问题欢迎报告"
617
+ }
618
+ }
619
+ ];
620
+ var vendorOptionsDescribe = `## 通用参数
621
+ - \`extraOptions\`字段用于覆盖默认字段
622
+ \`\`\`json
623
+ {"厂商名(与vendor字段相同)":{ "extraOptions":{"temperature":0.1,"topP":0.8,"maxTokens":8192,"baseURL":"http://127.0.0.1:11434/v1","apiKey":" "} }
624
+ \`\`\`
625
+ `;
626
+ var InputWrapper = metadataPipe(patchWrappers(["tooltip", "form-field"]));
627
+ var ChatItemDefine = v7.pipe(
628
+ v7.intersect([
629
+ v7.pipe(
630
+ v7.object({
631
+ name: v7.pipe(v7.string(), v7.title("配置名")),
632
+ vendor: v7.pipe(
633
+ v7.optional(
634
+ v7.picklist(VendorList.map((item) => item.value)),
635
+ "openai"
636
+ ),
637
+ v7.description("大语言模型提供的厂商"),
638
+ v7.title("对话厂商"),
639
+ v7.metadata({
640
+ enumOptions: VendorList.map((item) => {
641
+ return {
642
+ label: item.label,
643
+ description: [
644
+ item.options.description ? item.options.description : "",
645
+ item.options.baseURL ? `链接: ${item.options.baseURL}` : ""
646
+ ].filter(Boolean).join("\n")
647
+ };
648
+ })
649
+ }),
650
+ patchInputs({
651
+ options: VendorList
652
+ })
653
+ )
654
+ }),
655
+ componentClass("flex *:flex-1 gap-2 items-center")
656
+ ),
657
+ v7.pipe(
658
+ v7.object({
659
+ baseURL: v7.pipe(
660
+ v7.optional(v7.string(), "http://127.0.0.1:11434/v1"),
661
+ v7.title("地址"),
662
+ v7.description("openai兼容接口"),
663
+ ...InputWrapper
664
+ ),
665
+ model: v7.pipe(
666
+ v7.optional(v7.string(), `qwen3:8b`),
667
+ v7.title("模型"),
668
+ v7.description(
669
+ "## 模型名\n### ollama模型推荐\n- `qwen3:8b`\n- `qwen3:14b`,`deepseek-r1:7b`,`deepseek-r1:14b`\n### 图片对话模型\n- `minicpm-v:8b`"
670
+ ),
671
+ ...InputWrapper
672
+ )
673
+ }),
674
+ componentClass("flex *:flex-1 gap-2 items-center")
675
+ ),
676
+ v7.object({
677
+ apiKey: v7.pipe(
678
+ v7.optional(v7.string()),
679
+ v7.description("本地部署默认可以不填")
680
+ )
681
+ }),
682
+ v7.pipe(
683
+ v7.object({
684
+ max_tokens: v7.pipe(v7.optional(v7.number(), 8192)),
685
+ top_p: v7.pipe(
686
+ v7.optional(v7.number(), 0.8),
687
+ v7.minValue(0),
688
+ v7.maxValue(10),
689
+ v7.description(`核采样(nucleus sampling)是温度采样的另一种替代方法,模型会考虑累积概率质量达到 top_p 的 token。例如,当 top_p 设为 0.1 时,仅考虑累积概率前 10% 的 token。
690
+
691
+ 我们通常建议调整 top_p 或温度参数,但不要同时调整两者。`),
692
+ ...InputWrapper
693
+ ),
694
+ temperature: v7.pipe(
695
+ v7.optional(v7.pipe(v7.number(), v7.minValue(0), v7.maxValue(2)), 0.1),
696
+ v7.description(
697
+ "采样温度应设置在0到2之间。较高的值(如0.8)会使输出更随机,而较低的值(如0.2)则会使输出更集中且确定。我们通常建议仅调整此参数或top_p,而不同时调整两者。"
698
+ ),
699
+ ...InputWrapper
700
+ ),
701
+ frequency_penalty: v7.pipe(
702
+ v7.optional(v7.pipe(v7.number(), v7.minValue(-2), v7.maxValue(2))),
703
+ v7.description(
704
+ "取值范围为-2.0至2.0。正值会根据当前文本中已有标记的频率对新标记进行惩罚,从而降低模型逐字重复相同内容的概率。"
705
+ ),
706
+ ...InputWrapper
707
+ ),
708
+ presence_penalty: v7.pipe(
709
+ v7.optional(v7.pipe(v7.number(), v7.minValue(-2), v7.maxValue(2))),
710
+ v7.description(
711
+ "数值介于-2.0至2.0之间。正值会基于新标记是否已在当前文本中出现,对新标记施加惩罚,从而提升模型讨论新话题的可能性。"
712
+ ),
713
+ ...InputWrapper
714
+ ),
715
+ seed: v7.pipe(
716
+ v7.optional(v7.pipe(v7.number())),
717
+ v7.description(
718
+ "若已指定,系统将尽力确保采样具有确定性,即使用相同种子和参数的重复请求将返回相同结果。确定性无法保证,请通过 system_fingerprint 响应参数监控后端变化。"
719
+ ),
720
+ ...InputWrapper
721
+ ),
722
+ stop: v7.pipe(
723
+ v7.optional(v7.pipe(v7.array(v7.pipe(v7.string())), v7.maxLength(4))),
724
+ v7.description(
725
+ "最多可指定4个停止序列,API将在生成到该序列时停止,返回的文本中不包含该停止序列。"
726
+ ),
727
+ asControl(),
728
+ setComponent("chip-input-list"),
729
+ patchInputs({
730
+ addOnBlur: true
731
+ }),
732
+ ...InputWrapper
733
+ )
734
+ // top_logprobs: v.pipe(
735
+ // v.optional(v.pipe(v.number(), v.minValue(0), v.maxValue(20))),
736
+ // v.description(
737
+ // '一个介于0和20之间的整数,用于指定在每个标记位置返回的最可能的标记数量,每个标记均关联对应的对数概率。若使用此参数,则必须将logprobs设置为true。',
738
+ // ),
739
+ // ),
740
+ // verbosity: v.pipe(
741
+ // v.optional(v.picklist(['low', 'medium', 'high'])),
742
+ // v.description(
743
+ // `控制模型回复的详细程度。数值越低,回复越简洁;数值越高,回复越详细。当前支持的取值为低、中和高。`,
744
+ // ),
745
+ // ),
746
+ // reasoning_effort: v.pipe(
747
+ // v.optional(v.picklist(['minimal', 'low', 'medium', 'high'])),
748
+ // v.description(
749
+ // `控制模型回复的详细程度。数值越低,回复越简洁;数值越高,回复越详细。当前支持的取值为低、中和高。`,
750
+ // ),
751
+ // ),
752
+ }),
753
+ componentClass("grid gap-2")
754
+ )
755
+ ]),
756
+ componentClass("grid gap-2"),
757
+ asVirtualGroup()
758
+ );
759
+ var VendorOptionsDefine = v7.pipe(
760
+ v7.optional(
761
+ v7.record(
762
+ v7.string(),
763
+ v7.pipe(
764
+ v7.intersect([
765
+ v7.object({
766
+ /** @internal */
767
+ extraOptions: v7.pipe(
768
+ v7.optional(omitIntersect(ChatItemDefine, ["name", "vendor"])),
769
+ v7.description(`附加配置`)
770
+ )
771
+ })
772
+ ]),
773
+ componentClass("grid gap-2"),
774
+ asVirtualGroup()
775
+ )
776
+ )
777
+ ),
778
+ v7.description(vendorOptionsDescribe),
779
+ setComponent("rest-chip-group"),
780
+ patchInputs({
781
+ optionalkeyList: VendorList.map((item) => item.value),
782
+ placeholder: "请设置额外配置"
783
+ }),
784
+ v7.title("厂商额外配置")
785
+ );
786
+ var ChatParamsItemDefine = v7.pipe(
787
+ v7.intersect([
788
+ ChatItemDefine,
789
+ v7.object({ vendorOptions: VendorOptionsDefine })
790
+ ]),
791
+ asVirtualGroup(),
792
+ componentClass("grid gap-2")
793
+ );
794
+ var ChatParamsListDefine = v7.pipe(
795
+ v7.array(ChatParamsItemDefine),
796
+ setComponent("label-chip-array"),
797
+ v7.description("对话模型列表,目前用于切换使用"),
798
+ patchInputs({
799
+ displayKey: "name",
800
+ placeholder: "请添加配置"
801
+ })
802
+ );
803
+ var InputChatOptionsDefine = omitIntersect(ChatParamsItemDefine, [
804
+ "name"
805
+ ]);
806
+
807
+ // packages/openai/chat/token.ts
808
+ import { InjectionToken } from "static-injector";
809
+ var OpenAIConfigToken = new InjectionToken(
810
+ "OpenAIConfig"
811
+ );
812
+
813
+ // packages/openai/chat/chat.history.service.ts
814
+ import {
815
+ computed,
816
+ inject,
817
+ signal
818
+ } from "static-injector";
819
+ import { omitBy } from "es-toolkit";
820
+ import { bufferWhen, debounceTime, filter, Subject } from "rxjs";
821
+ import { path } from "@cyia/vfs2";
822
+ import * as fs from "fs";
823
+ import { parse as parse4, stringify } from "yaml";
824
+ import dayjs from "dayjs";
825
+ import { isEmptyInput } from "@cyia/util";
826
+ var ChatHistoryService = class {
827
+ #update$ = signal(0);
828
+ /** 外部监听更新用 */
829
+ update$$ = computed(() => this.#update$());
830
+ #logMap = /* @__PURE__ */ new Map();
831
+ #config = inject(OpenAIConfigToken);
832
+ #dir() {
833
+ return this.#config().history.dir;
834
+ }
835
+ #createNewListen(fileName) {
836
+ const filePath = path.join(this.#dir(), `${fileName}.yml`);
837
+ const instance = new Subject();
838
+ instance.pipe(
839
+ bufferWhen(() => instance.pipe(debounceTime(2e3))),
840
+ filter((list) => !!list.length)
841
+ ).subscribe(async (inputList) => {
842
+ if (!fs.existsSync(this.#dir())) {
843
+ await fs.promises.mkdir(this.#dir(), { recursive: true });
844
+ }
845
+ try {
846
+ let list = [];
847
+ if (await fs.existsSync(filePath)) {
848
+ const content = await fs.promises.readFile(filePath, {
849
+ encoding: "utf-8"
850
+ });
851
+ list = parse4(content);
852
+ } else {
853
+ list = [];
854
+ }
855
+ list.unshift(...inputList.reverse());
856
+ await fs.promises.writeFile(filePath, stringify(list));
857
+ this.#update$.update((a) => a + 1);
858
+ } catch (error) {
859
+ this.#config().captureException(error);
860
+ }
861
+ });
862
+ this.#logMap.set(fileName, instance);
863
+ return instance;
864
+ }
865
+ save(messages, options, config) {
866
+ if (!this.#config().history.enable) {
867
+ return;
868
+ }
869
+ try {
870
+ const fileName = dayjs().format("YYYY-MM-DD");
871
+ const message$ = this.#logMap.get(fileName) ?? this.#createNewListen(fileName);
872
+ const item = {
873
+ date: dayjs().valueOf(),
874
+ messages,
875
+ options: omitBy(options, isEmptyInput),
876
+ config
877
+ };
878
+ message$.next(item);
879
+ } catch (error) {
880
+ try {
881
+ this.#config().captureException(error);
882
+ } catch (error2) {
883
+ }
884
+ }
885
+ }
886
+ };
887
+
888
+ // packages/openai/chat/service.ts
889
+ import * as v8 from "valibot";
890
+ var ChatProviderService = class {
891
+ #config = inject2(OpenAIConfigToken);
892
+ #chatHistory = inject2(ChatHistoryService);
893
+ create(input) {
894
+ let options = v8.parse(InputChatOptionsDefine, input);
895
+ let instance;
896
+ const extraOptions = options.vendorOptions?.[options.vendor]?.["extraOptions"];
897
+ options = {
898
+ ...extraOptions,
899
+ ...options,
900
+ apiKey: options.apiKey?.trim() || extraOptions?.apiKey || " "
901
+ };
902
+ if (!options.vendor || options.vendor === "openai") {
903
+ instance = new OpenAIChat(options);
904
+ } else if (options.vendor === "gemini") {
905
+ instance = new GeminiChat(options);
906
+ } else if (options.vendor === "claude") {
907
+ instance = new AnthropicChat(options);
908
+ } else {
909
+ instance = createCompatibleFactory(options);
910
+ }
911
+ instance.init();
912
+ let openAIOptions = this.#config;
913
+ const chatHistory = this.#chatHistory;
914
+ const fn = async function* (...args) {
915
+ const result = instance.stream(args[0], {
916
+ ...args[1],
917
+ tryPull: openAIOptions().tryPull,
918
+ pullModel: openAIOptions().pullModel
919
+ });
920
+ let content = "";
921
+ let isThink = false;
922
+ let thinkStart = 0;
923
+ let thinkEnd;
924
+ let start = true;
925
+ let isThinking = false;
926
+ let contentEnd;
927
+ let lastEmit;
928
+ for await (const delta of result) {
929
+ content += delta;
930
+ if (start) {
931
+ start = false;
932
+ const result2 = ThinkList.find(
933
+ (item) => delta.startsWith(`<${item}>`)
934
+ );
935
+ if (result2) {
936
+ isThink = true;
937
+ thinkStart = result2.length + 2;
938
+ isThinking = true;
939
+ }
940
+ } else if (isThink && thinkEnd === void 0) {
941
+ const result2 = ThinkList.find((item) => delta === `</${item}>`);
942
+ if (result2) {
943
+ contentEnd = content.length;
944
+ thinkEnd = content.length - result2.length - 3;
945
+ isThinking = false;
946
+ }
947
+ }
948
+ lastEmit = {
949
+ content: isThink ? (contentEnd ? content.slice(contentEnd) : "").trim() : content,
950
+ isThinking,
951
+ delta,
952
+ thinkContent: isThink ? content.slice(thinkStart, thinkEnd).trim() : void 0
953
+ };
954
+ yield lastEmit;
955
+ }
956
+ const lastMessage = createAssistantMessage(
957
+ isThink ? contentEnd ? content.slice(contentEnd) : "" : content
958
+ );
959
+ if (lastEmit?.thinkContent) {
960
+ lastMessage.thinkContent = lastEmit.thinkContent;
961
+ }
962
+ chatHistory.save(
963
+ [...args[0].messages, lastMessage],
964
+ { ...args[0], messages: void 0 },
965
+ options
966
+ );
967
+ };
968
+ return {
969
+ stream: fn,
970
+ chat: async (...args) => {
971
+ const result = fn(...args);
972
+ let obj;
973
+ for await (const element of result) {
974
+ obj = element;
975
+ }
976
+ return obj;
977
+ },
978
+ callTool: (input2, options2) => {
979
+ return instance.callTool(input2, options2);
980
+ }
981
+ };
982
+ }
983
+ };
984
+
985
+ // packages/openai/chat/module.ts
986
+ var OPENAI_MODULE = {
987
+ provider: [ChatProviderService, ChatHistoryService],
988
+ token: { OpenAIConfigToken }
989
+ };
990
+ export {
991
+ AssistantChatCompletionContent,
992
+ AssistantChatMessage,
993
+ ChatCompletionContentPart,
994
+ ChatCompletionContentPartImage,
995
+ ChatCompletionContentPartStr,
996
+ ChatHistoryService,
997
+ ChatItemDefine,
998
+ ChatMessageItemDefine,
999
+ ChatMessageListDefine,
1000
+ ChatParamsItemDefine,
1001
+ ChatParamsListDefine,
1002
+ ChatProviderService,
1003
+ InputChatOptionsDefine,
1004
+ OPENAI_MODULE,
1005
+ OpenAIConfigToken,
1006
+ SystemChatCompletionContent,
1007
+ SystemChatMessage,
1008
+ UserChatCompletionContent,
1009
+ UserChatMessage,
1010
+ VendorOptionsDefine,
1011
+ createAssistantMessage,
1012
+ createSystemMessage,
1013
+ createUserMessage
1014
+ };
1015
+ //# sourceMappingURL=index.mjs.map