@kenkaiiii/gg-ai 5.23.3 → 5.25.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/dist/index.cjs CHANGED
@@ -42,6 +42,7 @@ __export(index_exports, {
42
42
  formatErrorForDisplay: () => formatErrorForDisplay,
43
43
  isHardBillingMessage: () => isHardBillingMessage,
44
44
  isUsageLimitError: () => isUsageLimitError,
45
+ localWireModelId: () => localWireModelId,
45
46
  palsuAssistantMessage: () => palsuAssistantMessage,
46
47
  palsuText: () => palsuText,
47
48
  palsuThinking: () => palsuThinking,
@@ -561,6 +562,39 @@ function normalizeRootForAnthropic(schema) {
561
562
  return out;
562
563
  }
563
564
 
565
+ // src/providers/reasoning-field.ts
566
+ var REASONING_FIELD_ALIASES = [
567
+ "reasoning_content",
568
+ "reasoning",
569
+ "reasoning_text"
570
+ ];
571
+ var DEFAULT_REASONING_FIELD = REASONING_FIELD_ALIASES[0];
572
+ function readReasoning(obj) {
573
+ if (!obj) return void 0;
574
+ for (const field of REASONING_FIELD_ALIASES) {
575
+ const value = obj[field];
576
+ if (typeof value === "string" && value) return { field, text: value };
577
+ }
578
+ return void 0;
579
+ }
580
+ function reasoningFieldKey(provider, baseUrl, model) {
581
+ return `${provider}|${baseUrl ?? ""}|${model}`;
582
+ }
583
+ var MAX_REMEMBERED_ENDPOINTS = 64;
584
+ var detectedFields = /* @__PURE__ */ new Map();
585
+ function rememberReasoningField(key, field) {
586
+ if (detectedFields.get(key) === field) return;
587
+ detectedFields.set(key, field);
588
+ while (detectedFields.size > MAX_REMEMBERED_ENDPOINTS) {
589
+ const oldest = detectedFields.keys().next();
590
+ if (oldest.done) break;
591
+ detectedFields.delete(oldest.value);
592
+ }
593
+ }
594
+ function getReasoningField(key) {
595
+ return detectedFields.get(key) ?? DEFAULT_REASONING_FIELD;
596
+ }
597
+
564
598
  // src/providers/transform.ts
565
599
  function hasValidThinkingSignature(part) {
566
600
  return typeof part.signature === "string" && part.signature.trim().length > 0;
@@ -981,6 +1015,7 @@ function remapToolCallId(id, idMap) {
981
1015
  return mapped;
982
1016
  }
983
1017
  function toOpenAIMessages(messages, options) {
1018
+ const reasoningField = options?.reasoningField || DEFAULT_REASONING_FIELD;
984
1019
  const out = [];
985
1020
  const idMap = /* @__PURE__ */ new Map();
986
1021
  const mergeToolResultText = options?.provider === "glm";
@@ -1047,9 +1082,9 @@ function toOpenAIMessages(messages, options) {
1047
1082
  ...hasToolCalls ? { tool_calls: toolCalls } : {}
1048
1083
  };
1049
1084
  if (thinkingParts) {
1050
- assistantMsg.reasoning_content = thinkingParts;
1085
+ assistantMsg[reasoningField] = thinkingParts;
1051
1086
  } else if (options?.thinking && hasToolCalls && options.provider !== "glm") {
1052
- assistantMsg.reasoning_content = " ";
1087
+ assistantMsg[reasoningField] = " ";
1053
1088
  }
1054
1089
  out.push(assistantMsg);
1055
1090
  continue;
@@ -1127,6 +1162,10 @@ function toOpenAIToolChoice(choice) {
1127
1162
  if (choice === "required") return "required";
1128
1163
  return { type: "function", function: { name: choice.name } };
1129
1164
  }
1165
+ function toLocalReasoningEffort(level) {
1166
+ if (level === "max" || level === "ultra" || level === "xhigh") return "max";
1167
+ return level;
1168
+ }
1130
1169
  function toOpenAIReasoningEffort(level, model) {
1131
1170
  const effort = level === "max" || level === "ultra" ? "xhigh" : level;
1132
1171
  if (model.startsWith("fugu") && (effort === "low" || effort === "medium")) {
@@ -1922,7 +1961,9 @@ function streamOpenAI(options) {
1922
1961
  async function* runStream2(options) {
1923
1962
  const providerName = options.provider ?? "openai";
1924
1963
  const useStreaming = options.streaming !== false;
1964
+ const endpointKey = reasoningFieldKey(providerName, options.baseUrl, options.model);
1925
1965
  const client = createClient2(options);
1966
+ const isLocal = options.provider === "local";
1926
1967
  const isKimiK3 = options.provider === "moonshot" && options.model === "kimi-k3";
1927
1968
  const isManagedKimiK3 = isKimiK3 && options.baseUrl?.replace(/\/+$/, "").endsWith("/coding/v1") === true;
1928
1969
  const k3Effort = options.thinking ? toKimiK3Effort(options.thinking) : void 0;
@@ -1945,7 +1986,8 @@ async function* runStream2(options) {
1945
1986
  // disabled K3 must NOT carry placeholder reasoning_content (mirrors the
1946
1987
  // official CLI: reasoning is preserved only while thinking is enabled).
1947
1988
  thinking: isKimiK27 || !!options.thinking,
1948
- supportsImages: options.supportsImages
1989
+ supportsImages: options.supportsImages,
1990
+ reasoningField: getReasoningField(endpointKey)
1949
1991
  });
1950
1992
  const defaultTemp = options.provider === "glm" ? 0.6 : void 0;
1951
1993
  const effectiveTemp = options.temperature ?? defaultTemp;
@@ -1957,7 +1999,7 @@ async function* runStream2(options) {
1957
1999
  ...effectiveTemp != null && !options.thinking && !hasFixedKimiSampling ? { temperature: effectiveTemp } : {},
1958
2000
  ...options.topP != null && !hasFixedKimiSampling ? { top_p: options.topP } : {},
1959
2001
  ...options.stop ? { stop: options.stop } : {},
1960
- ...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
2002
+ ...options.thinking && !usesThinkingParam && !isKimiK3 && !isKimiK27 && !isLocal ? { reasoning_effort: toOpenAIReasoningEffort(options.thinking, options.model) } : {},
1961
2003
  ...options.tools?.length ? { tools: toOpenAITools(options.tools) } : {},
1962
2004
  ...options.toolChoice && options.tools?.length ? { tool_choice: toOpenAIToolChoice(options.toolChoice) } : {},
1963
2005
  ...useStreaming ? { stream_options: { include_usage: true } } : {}
@@ -1971,6 +2013,11 @@ async function* runStream2(options) {
1971
2013
  paramsAny.prompt_cache_retention = "24h";
1972
2014
  }
1973
2015
  }
2016
+ if (isLocal && options.thinking) {
2017
+ params.reasoning_effort = toLocalReasoningEffort(
2018
+ options.thinking
2019
+ );
2020
+ }
1974
2021
  if (options.provider === "openai" && options.serviceTier) {
1975
2022
  params.service_tier = options.serviceTier;
1976
2023
  }
@@ -2007,8 +2054,8 @@ async function* runStream2(options) {
2007
2054
  const completion = await client.chat.completions.create(params, {
2008
2055
  signal: options.signal ?? void 0
2009
2056
  });
2010
- yield* synthesizeEventsFromCompletion(completion, !!options.thinking);
2011
- return completionToResponse(completion);
2057
+ yield* synthesizeEventsFromCompletion(completion, !!options.thinking, endpointKey);
2058
+ return completionToResponse(completion, endpointKey);
2012
2059
  } catch (err) {
2013
2060
  throw toError2(err, providerName);
2014
2061
  }
@@ -2043,11 +2090,12 @@ async function* runStream2(options) {
2043
2090
  finishReason = choice.finish_reason;
2044
2091
  }
2045
2092
  const delta = choice.delta;
2046
- const reasoningContent = delta.reasoning_content;
2047
- if (typeof reasoningContent === "string" && reasoningContent) {
2048
- thinkingAccum += reasoningContent;
2093
+ const reasoning = readReasoning(delta);
2094
+ if (reasoning) {
2095
+ rememberReasoningField(endpointKey, reasoning.field);
2096
+ thinkingAccum += reasoning.text;
2049
2097
  if (options.thinking) {
2050
- yield { type: "thinking_delta", text: reasoningContent };
2098
+ yield { type: "thinking_delta", text: reasoning.text };
2051
2099
  }
2052
2100
  }
2053
2101
  if (delta.content) {
@@ -2132,16 +2180,17 @@ async function* runStream2(options) {
2132
2180
  yield { type: "done", stopReason };
2133
2181
  return response;
2134
2182
  }
2135
- function* synthesizeEventsFromCompletion(completion, thinkingEnabled) {
2183
+ function* synthesizeEventsFromCompletion(completion, thinkingEnabled, endpointKey) {
2136
2184
  const choice = completion.choices?.[0];
2137
2185
  if (!choice) {
2138
2186
  yield { type: "done", stopReason: normalizeOpenAIStopReason(null) };
2139
2187
  return;
2140
2188
  }
2141
2189
  const msg = choice.message;
2142
- const reasoning = msg.reasoning_content;
2143
- if (typeof reasoning === "string" && reasoning && thinkingEnabled) {
2144
- yield { type: "thinking_delta", text: reasoning };
2190
+ const reasoning = readReasoning(msg);
2191
+ if (reasoning) {
2192
+ rememberReasoningField(endpointKey, reasoning.field);
2193
+ if (thinkingEnabled) yield { type: "thinking_delta", text: reasoning.text };
2145
2194
  }
2146
2195
  if (typeof msg.content === "string" && msg.content) {
2147
2196
  yield { type: "text_delta", text: msg.content };
@@ -2169,15 +2218,16 @@ function* synthesizeEventsFromCompletion(completion, thinkingEnabled) {
2169
2218
  }
2170
2219
  yield { type: "done", stopReason: normalizeOpenAIStopReason(choice.finish_reason ?? null) };
2171
2220
  }
2172
- function completionToResponse(completion) {
2221
+ function completionToResponse(completion, endpointKey) {
2173
2222
  const choice = completion.choices?.[0];
2174
2223
  const contentParts = [];
2175
2224
  let textAccum = "";
2176
2225
  if (choice) {
2177
2226
  const msg = choice.message;
2178
- const reasoning = msg.reasoning_content;
2179
- if (typeof reasoning === "string" && reasoning) {
2180
- contentParts.push({ type: "thinking", text: reasoning });
2227
+ const reasoning = readReasoning(msg);
2228
+ if (reasoning) {
2229
+ rememberReasoningField(endpointKey, reasoning.field);
2230
+ contentParts.push({ type: "thinking", text: reasoning.text });
2181
2231
  }
2182
2232
  if (typeof msg.content === "string" && msg.content) {
2183
2233
  textAccum = msg.content;
@@ -3529,6 +3579,28 @@ providerRegistry.register("minimax", {
3529
3579
  serverTools: void 0
3530
3580
  })
3531
3581
  });
3582
+ function localWireModelId(id) {
3583
+ const match = /^local\/[^/]+\/(.+)$/.exec(id);
3584
+ return match?.[1] ?? id;
3585
+ }
3586
+ providerRegistry.register("local", {
3587
+ // Locally hosted OpenAI-compatible servers (Ollama, LM Studio, llama.cpp,
3588
+ // vLLM). There is no default endpoint: the baseUrl comes from the endpoint
3589
+ // credential the discovery layer wrote, so a missing one is a wiring bug, not
3590
+ // something to paper over with a guess at someone else's port.
3591
+ stream: (options) => {
3592
+ if (!options.baseUrl) {
3593
+ throw new GGAIError(
3594
+ "Local provider requires a baseUrl (e.g. http://127.0.0.1:11434/v1). No local endpoint was resolved for this model \u2014 re-scan for local models."
3595
+ );
3596
+ }
3597
+ return streamOpenAI({
3598
+ ...options,
3599
+ model: localWireModelId(options.model),
3600
+ webSearch: false
3601
+ });
3602
+ }
3603
+ });
3532
3604
  function stream(options) {
3533
3605
  const entry = providerRegistry.get(options.provider);
3534
3606
  if (!entry) {
@@ -3933,6 +4005,7 @@ function registerPalsuProvider(config) {
3933
4005
  formatErrorForDisplay,
3934
4006
  isHardBillingMessage,
3935
4007
  isUsageLimitError,
4008
+ localWireModelId,
3936
4009
  palsuAssistantMessage,
3937
4010
  palsuText,
3938
4011
  palsuThinking,