@lobehub/chat 1.106.1 → 1.106.3

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.
@@ -7,7 +7,7 @@ export interface ModelProcessorConfig {
7
7
  visionKeywords?: readonly string[];
8
8
  }
9
9
 
10
- // 模型标签关键词配置
10
+ // 模型能力标签关键词配置
11
11
  export const MODEL_LIST_CONFIGS = {
12
12
  anthropic: {
13
13
  functionCallKeywords: ['claude'],
@@ -64,7 +64,7 @@ export const MODEL_LIST_CONFIGS = {
64
64
  },
65
65
  zhipu: {
66
66
  functionCallKeywords: ['glm-4', 'glm-z1'],
67
- reasoningKeywords: ['glm-zero', 'glm-z1'],
67
+ reasoningKeywords: ['glm-zero', 'glm-z1', 'glm-4.5'],
68
68
  visionKeywords: ['glm-4v'],
69
69
  },
70
70
  } as const;
@@ -1,6 +1,8 @@
1
1
  import { ModelProvider } from '../types';
2
2
  import { MODEL_LIST_CONFIGS, processModelList } from '../utils/modelParse';
3
3
  import { createOpenAICompatibleRuntime } from '../utils/openaiCompatibleFactory';
4
+ import { OpenAIStream } from '../utils/streams/openai';
5
+ import { convertIterableToStream } from '../utils/streams/protocol';
4
6
 
5
7
  export interface ZhipuModelCard {
6
8
  description: string;
@@ -12,7 +14,8 @@ export const LobeZhipuAI = createOpenAICompatibleRuntime({
12
14
  baseURL: 'https://open.bigmodel.cn/api/paas/v4',
13
15
  chatCompletion: {
14
16
  handlePayload: (payload) => {
15
- const { enabledSearch, max_tokens, model, temperature, tools, top_p, ...rest } = payload;
17
+ const { enabledSearch, max_tokens, model, temperature, thinking, tools, top_p, ...rest } =
18
+ payload;
16
19
 
17
20
  const zhipuTools = enabledSearch
18
21
  ? [
@@ -39,6 +42,7 @@ export const LobeZhipuAI = createOpenAICompatibleRuntime({
39
42
  max_tokens,
40
43
  model,
41
44
  stream: true,
45
+ thinking: model.includes('-4.5') ? { type: thinking?.type } : undefined,
42
46
  tools: zhipuTools,
43
47
  ...(model === 'glm-4-alltools'
44
48
  ? {
@@ -54,6 +58,58 @@ export const LobeZhipuAI = createOpenAICompatibleRuntime({
54
58
  }),
55
59
  } as any;
56
60
  },
61
+ handleStream: (stream, { callbacks, inputStartAt }) => {
62
+ const readableStream =
63
+ stream instanceof ReadableStream ? stream : convertIterableToStream(stream);
64
+
65
+ // GLM-4.5 系列模型在 tool_calls 中返回的 index 为 -1,需要在进入 OpenAIStream 之前修正
66
+ // 因为 OpenAIStream 内部会过滤掉 index < 0 的 tool_calls (openai.ts:58-60)
67
+ const preprocessedStream = readableStream.pipeThrough(
68
+ new TransformStream({
69
+ transform(chunk, controller) {
70
+ // 处理原始的 OpenAI ChatCompletionChunk 格式
71
+ if (chunk.choices && chunk.choices[0]) {
72
+ const choice = chunk.choices[0];
73
+ if (choice.delta?.tool_calls && Array.isArray(choice.delta.tool_calls)) {
74
+ // 修正负数 index,将 -1 转换为基于数组位置的正数 index
75
+ const fixedToolCalls = choice.delta.tool_calls.map(
76
+ (toolCall: any, globalIndex: number) => ({
77
+ ...toolCall,
78
+ index: toolCall.index < 0 ? globalIndex : toolCall.index,
79
+ }),
80
+ );
81
+
82
+ // 创建修正后的 chunk
83
+ const fixedChunk = {
84
+ ...chunk,
85
+ choices: [
86
+ {
87
+ ...choice,
88
+ delta: {
89
+ ...choice.delta,
90
+ tool_calls: fixedToolCalls,
91
+ },
92
+ },
93
+ ],
94
+ };
95
+
96
+ controller.enqueue(fixedChunk);
97
+ } else {
98
+ controller.enqueue(chunk);
99
+ }
100
+ } else {
101
+ controller.enqueue(chunk);
102
+ }
103
+ },
104
+ }),
105
+ );
106
+
107
+ return OpenAIStream(preprocessedStream, {
108
+ callbacks,
109
+ inputStartAt,
110
+ provider: 'zhipu',
111
+ });
112
+ },
57
113
  },
58
114
  debug: {
59
115
  chatCompletion: () => process.env.DEBUG_ZHIPU_CHAT_COMPLETION === '1',
@@ -0,0 +1,61 @@
1
+ import debug from 'debug';
2
+ import { NextRequest } from 'next/server';
3
+
4
+ const log = debug('lobe-oidc:correctOIDCUrl');
5
+
6
+ /**
7
+ * 修复 OIDC 重定向 URL 在代理环境下的问题
8
+ * @param req - Next.js 请求对象
9
+ * @param url - 要修复的 URL 对象
10
+ * @returns 修复后的 URL 对象
11
+ */
12
+ export const correctOIDCUrl = (req: NextRequest, url: URL): URL => {
13
+ const requestHost = req.headers.get('host');
14
+ const forwardedHost = req.headers.get('x-forwarded-host');
15
+ const forwardedProto =
16
+ req.headers.get('x-forwarded-proto') || req.headers.get('x-forwarded-protocol');
17
+
18
+ log('Input URL: %s', url.toString());
19
+ log(
20
+ 'Request headers - host: %s, x-forwarded-host: %s, x-forwarded-proto: %s',
21
+ requestHost,
22
+ forwardedHost,
23
+ forwardedProto,
24
+ );
25
+
26
+ // 确定实际的主机名和协议,提供后备值
27
+ const actualHost = forwardedHost || requestHost;
28
+ const actualProto = forwardedProto || (url.protocol === 'https:' ? 'https' : 'http');
29
+
30
+ // 如果无法确定有效的主机名,直接返回原URL
31
+ if (!actualHost || actualHost === 'null') {
32
+ log('Warning: Cannot determine valid host, returning original URL');
33
+ return url;
34
+ }
35
+
36
+ // 如果 URL 指向本地地址,或者主机名与实际请求主机不匹配,则修正 URL
37
+ const needsCorrection =
38
+ url.hostname === 'localhost' ||
39
+ url.hostname === '127.0.0.1' ||
40
+ url.hostname === '0.0.0.0' ||
41
+ url.hostname !== actualHost;
42
+
43
+ if (needsCorrection) {
44
+ log('URL needs correction. Original hostname: %s, correcting to: %s', url.hostname, actualHost);
45
+
46
+ try {
47
+ const correctedUrl = new URL(url.toString());
48
+ correctedUrl.protocol = actualProto + ':';
49
+ correctedUrl.host = actualHost;
50
+
51
+ log('Corrected URL: %s', correctedUrl.toString());
52
+ return correctedUrl;
53
+ } catch (error) {
54
+ log('Error creating corrected URL, returning original: %O', error);
55
+ return url;
56
+ }
57
+ }
58
+
59
+ log('URL does not need correction, returning original: %s', url.toString());
60
+ return url;
61
+ };