@theokit/sdk 2.11.2 → 2.12.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/a2a/index.js CHANGED
@@ -10094,6 +10094,40 @@ var init_ollama_native = __esm({
10094
10094
  }
10095
10095
  });
10096
10096
 
10097
+ // src/internal/llm/hermes-tool-extract.ts
10098
+ function extractHermesToolCalls(content, makeId) {
10099
+ const toolCalls = [];
10100
+ for (const block of content.matchAll(HERMES_BLOCK)) {
10101
+ const name = (block[1] ?? "").trim();
10102
+ if (name.length === 0) continue;
10103
+ toolCalls.push({
10104
+ type: "tool_use",
10105
+ id: makeId(),
10106
+ name,
10107
+ input: parseHermesParams(block[2] ?? "")
10108
+ });
10109
+ }
10110
+ const residualText = toolCalls.length === 0 ? content : content.replace(HERMES_BLOCK, "").trim();
10111
+ return { toolCalls, residualText };
10112
+ }
10113
+ function parseHermesParams(inner) {
10114
+ const input = {};
10115
+ for (const param of inner.matchAll(HERMES_PARAM)) {
10116
+ const key = param[1];
10117
+ const value = param[2];
10118
+ if (key === void 0 || value === void 0) continue;
10119
+ input[key.trim()] = value;
10120
+ }
10121
+ return input;
10122
+ }
10123
+ var HERMES_BLOCK, HERMES_PARAM;
10124
+ var init_hermes_tool_extract = __esm({
10125
+ "src/internal/llm/hermes-tool-extract.ts"() {
10126
+ HERMES_BLOCK = /<function=\s*([^>\s]+)\s*>([\s\S]*?)<\/tool_call>/g;
10127
+ HERMES_PARAM = /<parameter=\s*([^>\s]+)\s*>([\s\S]*?)<\/parameter>/g;
10128
+ }
10129
+ });
10130
+
10097
10131
  // src/internal/llm/openai.ts
10098
10132
  function mapOpenAIFinish(reason) {
10099
10133
  switch (reason) {
@@ -10206,6 +10240,7 @@ var init_openai2 = __esm({
10206
10240
  init_ollama2();
10207
10241
  init_openai_compatible();
10208
10242
  init_finish();
10243
+ init_hermes_tool_extract();
10209
10244
  init_sse();
10210
10245
  OpenAIClient = class {
10211
10246
  constructor(options) {
@@ -10267,7 +10302,10 @@ var init_openai2 = __esm({
10267
10302
  endpoint: "/v1/chat/completions"
10268
10303
  });
10269
10304
  }
10270
- const accumulator = new OpenAIStreamAccumulator();
10305
+ const accumulator = new OpenAIStreamAccumulator(
10306
+ this.options.extractToolCallsFromContent ?? false,
10307
+ providerId
10308
+ );
10271
10309
  for await (const record of parseSseStream(response.body, signal)) {
10272
10310
  if (record.data === "[DONE]") break;
10273
10311
  let chunk;
@@ -10276,6 +10314,16 @@ var init_openai2 = __esm({
10276
10314
  } catch {
10277
10315
  continue;
10278
10316
  }
10317
+ if (chunk.error !== void 0 && chunk.error !== null) {
10318
+ const status = typeof chunk.error.code === "number" ? chunk.error.code : 502;
10319
+ throw mapOpenAICompatibleError({
10320
+ providerId,
10321
+ status,
10322
+ body: chunk,
10323
+ headers: response.headers,
10324
+ endpoint: "/v1/chat/completions"
10325
+ });
10326
+ }
10279
10327
  const events = accumulator.consume(chunk);
10280
10328
  for (const event of events) yield event;
10281
10329
  }
@@ -10283,6 +10331,16 @@ var init_openai2 = __esm({
10283
10331
  }
10284
10332
  };
10285
10333
  OpenAIStreamAccumulator = class {
10334
+ /**
10335
+ * @param extractFromContent opt-in leaked-dialect safe-parse (theokit#58). Default false.
10336
+ * @param providerName provider id, used only to label the recovery log line.
10337
+ */
10338
+ constructor(extractFromContent = false, providerName = "openai") {
10339
+ this.extractFromContent = extractFromContent;
10340
+ this.providerName = providerName;
10341
+ }
10342
+ extractFromContent;
10343
+ providerName;
10286
10344
  text = "";
10287
10345
  stopReason = "end_turn";
10288
10346
  inputTokens;
@@ -10348,9 +10406,26 @@ var init_openai2 = __esm({
10348
10406
  const input = parseToolArguments(call.args);
10349
10407
  toolCalls.push({ type: "tool_use", id: call.id, name: call.name, input });
10350
10408
  }
10409
+ let text = this.text;
10410
+ let stopReason = this.stopReason;
10411
+ if (this.extractFromContent && toolCalls.length === 0) {
10412
+ const recovered = extractHermesToolCalls(
10413
+ this.text,
10414
+ () => `hermes-${globalThis.crypto.randomUUID()}`
10415
+ );
10416
+ if (recovered.toolCalls.length > 0) {
10417
+ toolCalls.push(...recovered.toolCalls);
10418
+ text = recovered.residualText;
10419
+ stopReason = "tool_use";
10420
+ process.stderr.write(
10421
+ `[theokit-sdk] recovered ${recovered.toolCalls.length} leaked tool call(s) from assistant content (provider="${this.providerName}", names=${recovered.toolCalls.map((c) => c.name).join(",")})
10422
+ `
10423
+ );
10424
+ }
10425
+ }
10351
10426
  return makeLlmFinish({
10352
- stopReason: this.stopReason,
10353
- text: this.text,
10427
+ stopReason,
10428
+ text,
10354
10429
  toolCalls,
10355
10430
  inputTokens: this.inputTokens,
10356
10431
  outputTokens: this.outputTokens,
@@ -10863,6 +10938,9 @@ function selectTransport(profile, apiKey) {
10863
10938
  const opts = { apiKey };
10864
10939
  opts.baseUrl = profile.baseUrl;
10865
10940
  opts.providerName = profile.name;
10941
+ if (profile.extractToolCallsFromContent === true) {
10942
+ opts.extractToolCallsFromContent = true;
10943
+ }
10866
10944
  if (profile.name === "openai" && process.env.OPENAI_ORGANIZATION !== void 0) {
10867
10945
  opts.organization = process.env.OPENAI_ORGANIZATION;
10868
10946
  }