@corbat-tech/coco 2.8.0 → 2.8.2

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.d.ts CHANGED
@@ -993,6 +993,8 @@ interface ChatOptions {
993
993
  stopSequences?: string[];
994
994
  system?: string;
995
995
  timeout?: number;
996
+ /** Abort signal to cancel in-flight requests */
997
+ signal?: AbortSignal;
996
998
  }
997
999
  /**
998
1000
  * Chat response
@@ -1030,6 +1032,7 @@ interface StreamChunk {
1030
1032
  type: "text" | "tool_use_start" | "tool_use_delta" | "tool_use_end" | "done";
1031
1033
  text?: string;
1032
1034
  toolCall?: Partial<ToolCall>;
1035
+ stopReason?: "end_turn" | "max_tokens" | "stop_sequence" | "tool_use";
1033
1036
  }
1034
1037
  /**
1035
1038
  * LLM Provider interface
package/dist/index.js CHANGED
@@ -11784,22 +11784,44 @@ var AnthropicProvider = class {
11784
11784
  async *stream(messages, options) {
11785
11785
  this.ensureInitialized();
11786
11786
  try {
11787
- const stream = await this.client.messages.stream({
11788
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11789
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11790
- temperature: options?.temperature ?? this.config.temperature ?? 0,
11791
- system: this.extractSystem(messages, options?.system),
11792
- messages: this.convertMessages(messages)
11793
- });
11794
- for await (const event of stream) {
11795
- if (event.type === "content_block_delta") {
11796
- const delta = event.delta;
11797
- if (delta.type === "text_delta" && delta.text) {
11798
- yield { type: "text", text: delta.text };
11787
+ const stream = await this.client.messages.stream(
11788
+ {
11789
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11790
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11791
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
11792
+ system: this.extractSystem(messages, options?.system),
11793
+ messages: this.convertMessages(messages)
11794
+ },
11795
+ { signal: options?.signal }
11796
+ );
11797
+ const streamTimeout = this.config.timeout ?? 12e4;
11798
+ let lastActivityTime = Date.now();
11799
+ const checkTimeout = () => {
11800
+ if (Date.now() - lastActivityTime > streamTimeout) {
11801
+ throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
11802
+ }
11803
+ };
11804
+ const timeoutInterval = setInterval(checkTimeout, 5e3);
11805
+ try {
11806
+ let streamStopReason;
11807
+ for await (const event of stream) {
11808
+ lastActivityTime = Date.now();
11809
+ if (event.type === "content_block_delta") {
11810
+ const delta = event.delta;
11811
+ if (delta.type === "text_delta" && delta.text) {
11812
+ yield { type: "text", text: delta.text };
11813
+ }
11814
+ } else if (event.type === "message_delta") {
11815
+ const delta = event.delta;
11816
+ if (delta.stop_reason) {
11817
+ streamStopReason = this.mapStopReason(delta.stop_reason);
11818
+ }
11799
11819
  }
11800
11820
  }
11821
+ yield { type: "done", stopReason: streamStopReason };
11822
+ } finally {
11823
+ clearInterval(timeoutInterval);
11801
11824
  }
11802
- yield { type: "done" };
11803
11825
  } catch (error) {
11804
11826
  throw this.handleError(error);
11805
11827
  }
@@ -11810,90 +11832,112 @@ var AnthropicProvider = class {
11810
11832
  async *streamWithTools(messages, options) {
11811
11833
  this.ensureInitialized();
11812
11834
  try {
11813
- const stream = await this.client.messages.stream({
11814
- model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11815
- max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11816
- temperature: options?.temperature ?? this.config.temperature ?? 0,
11817
- system: this.extractSystem(messages, options?.system),
11818
- messages: this.convertMessages(messages),
11819
- tools: this.convertTools(options.tools),
11820
- tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
11821
- });
11835
+ const stream = await this.client.messages.stream(
11836
+ {
11837
+ model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
11838
+ max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
11839
+ temperature: options?.temperature ?? this.config.temperature ?? 0,
11840
+ system: this.extractSystem(messages, options?.system),
11841
+ messages: this.convertMessages(messages),
11842
+ tools: this.convertTools(options.tools),
11843
+ tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
11844
+ },
11845
+ { signal: options?.signal }
11846
+ );
11822
11847
  let currentToolCall = null;
11823
11848
  let currentToolInputJson = "";
11824
- for await (const event of stream) {
11825
- if (event.type === "content_block_start") {
11826
- const contentBlock = event.content_block;
11827
- if (contentBlock.type === "tool_use") {
11849
+ const streamTimeout = this.config.timeout ?? 12e4;
11850
+ let lastActivityTime = Date.now();
11851
+ const checkTimeout = () => {
11852
+ if (Date.now() - lastActivityTime > streamTimeout) {
11853
+ throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
11854
+ }
11855
+ };
11856
+ const timeoutInterval = setInterval(checkTimeout, 5e3);
11857
+ try {
11858
+ let streamStopReason;
11859
+ for await (const event of stream) {
11860
+ lastActivityTime = Date.now();
11861
+ if (event.type === "message_delta") {
11862
+ const delta = event.delta;
11863
+ if (delta.stop_reason) {
11864
+ streamStopReason = this.mapStopReason(delta.stop_reason);
11865
+ }
11866
+ } else if (event.type === "content_block_start") {
11867
+ const contentBlock = event.content_block;
11868
+ if (contentBlock.type === "tool_use") {
11869
+ if (currentToolCall) {
11870
+ getLogger().warn(
11871
+ `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
11872
+ );
11873
+ try {
11874
+ currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11875
+ } catch {
11876
+ currentToolCall.input = {};
11877
+ }
11878
+ yield {
11879
+ type: "tool_use_end",
11880
+ toolCall: { ...currentToolCall }
11881
+ };
11882
+ }
11883
+ currentToolCall = {
11884
+ id: contentBlock.id,
11885
+ name: contentBlock.name
11886
+ };
11887
+ currentToolInputJson = "";
11888
+ yield {
11889
+ type: "tool_use_start",
11890
+ toolCall: { ...currentToolCall }
11891
+ };
11892
+ }
11893
+ } else if (event.type === "content_block_delta") {
11894
+ const delta = event.delta;
11895
+ if (delta.type === "text_delta" && delta.text) {
11896
+ yield { type: "text", text: delta.text };
11897
+ } else if (delta.type === "input_json_delta" && delta.partial_json) {
11898
+ currentToolInputJson += delta.partial_json;
11899
+ yield {
11900
+ type: "tool_use_delta",
11901
+ toolCall: {
11902
+ ...currentToolCall
11903
+ },
11904
+ text: delta.partial_json
11905
+ };
11906
+ }
11907
+ } else if (event.type === "content_block_stop") {
11828
11908
  if (currentToolCall) {
11829
- getLogger().warn(
11830
- `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
11831
- );
11832
11909
  try {
11833
11910
  currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11834
11911
  } catch {
11835
- currentToolCall.input = {};
11912
+ let repaired = false;
11913
+ if (currentToolInputJson) {
11914
+ try {
11915
+ currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
11916
+ repaired = true;
11917
+ getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
11918
+ } catch {
11919
+ }
11920
+ }
11921
+ if (!repaired) {
11922
+ getLogger().warn(
11923
+ `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
11924
+ );
11925
+ currentToolCall.input = {};
11926
+ }
11836
11927
  }
11837
11928
  yield {
11838
11929
  type: "tool_use_end",
11839
11930
  toolCall: { ...currentToolCall }
11840
11931
  };
11932
+ currentToolCall = null;
11933
+ currentToolInputJson = "";
11841
11934
  }
11842
- currentToolCall = {
11843
- id: contentBlock.id,
11844
- name: contentBlock.name
11845
- };
11846
- currentToolInputJson = "";
11847
- yield {
11848
- type: "tool_use_start",
11849
- toolCall: { ...currentToolCall }
11850
- };
11851
- }
11852
- } else if (event.type === "content_block_delta") {
11853
- const delta = event.delta;
11854
- if (delta.type === "text_delta" && delta.text) {
11855
- yield { type: "text", text: delta.text };
11856
- } else if (delta.type === "input_json_delta" && delta.partial_json) {
11857
- currentToolInputJson += delta.partial_json;
11858
- yield {
11859
- type: "tool_use_delta",
11860
- toolCall: {
11861
- ...currentToolCall
11862
- },
11863
- text: delta.partial_json
11864
- };
11865
- }
11866
- } else if (event.type === "content_block_stop") {
11867
- if (currentToolCall) {
11868
- try {
11869
- currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11870
- } catch {
11871
- let repaired = false;
11872
- if (currentToolInputJson) {
11873
- try {
11874
- currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
11875
- repaired = true;
11876
- getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
11877
- } catch {
11878
- }
11879
- }
11880
- if (!repaired) {
11881
- getLogger().warn(
11882
- `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
11883
- );
11884
- currentToolCall.input = {};
11885
- }
11886
- }
11887
- yield {
11888
- type: "tool_use_end",
11889
- toolCall: { ...currentToolCall }
11890
- };
11891
- currentToolCall = null;
11892
- currentToolInputJson = "";
11893
11935
  }
11894
11936
  }
11937
+ yield { type: "done", stopReason: streamStopReason };
11938
+ } finally {
11939
+ clearInterval(timeoutInterval);
11895
11940
  }
11896
- yield { type: "done" };
11897
11941
  } catch (error) {
11898
11942
  throw this.handleError(error);
11899
11943
  }
@@ -12386,13 +12430,18 @@ var OpenAIProvider = class {
12386
12430
  stream: true,
12387
12431
  ...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 }
12388
12432
  });
12433
+ let streamStopReason;
12389
12434
  for await (const chunk of stream) {
12390
12435
  const delta = chunk.choices[0]?.delta;
12391
12436
  if (delta?.content) {
12392
12437
  yield { type: "text", text: delta.content };
12393
12438
  }
12439
+ const finishReason = chunk.choices[0]?.finish_reason;
12440
+ if (finishReason) {
12441
+ streamStopReason = this.mapFinishReason(finishReason);
12442
+ }
12394
12443
  }
12395
- yield { type: "done" };
12444
+ yield { type: "done", stopReason: streamStopReason };
12396
12445
  } catch (error) {
12397
12446
  throw this.handleError(error);
12398
12447
  }
@@ -12457,6 +12506,7 @@ var OpenAIProvider = class {
12457
12506
  return input;
12458
12507
  };
12459
12508
  try {
12509
+ let streamStopReason;
12460
12510
  for await (const chunk of stream) {
12461
12511
  const delta = chunk.choices[0]?.delta;
12462
12512
  if (delta?.content || delta?.tool_calls) {
@@ -12503,6 +12553,9 @@ var OpenAIProvider = class {
12503
12553
  }
12504
12554
  }
12505
12555
  const finishReason = chunk.choices[0]?.finish_reason;
12556
+ if (finishReason) {
12557
+ streamStopReason = this.mapFinishReason(finishReason);
12558
+ }
12506
12559
  if (finishReason && toolCallBuilders.size > 0) {
12507
12560
  for (const [, builder] of toolCallBuilders) {
12508
12561
  yield {
@@ -12527,7 +12580,7 @@ var OpenAIProvider = class {
12527
12580
  }
12528
12581
  };
12529
12582
  }
12530
- yield { type: "done" };
12583
+ yield { type: "done", stopReason: streamStopReason };
12531
12584
  } finally {
12532
12585
  clearInterval(timeoutInterval);
12533
12586
  }
@@ -13290,7 +13343,7 @@ var CodexProvider = class {
13290
13343
  }
13291
13344
  }
13292
13345
  }
13293
- yield { type: "done" };
13346
+ yield { type: "done", stopReason: response.stopReason };
13294
13347
  }
13295
13348
  /**
13296
13349
  * Stream a chat response with tool use
@@ -13443,13 +13496,18 @@ var GeminiProvider = class {
13443
13496
  const { history, lastMessage } = this.convertMessages(messages);
13444
13497
  const chat = model.startChat({ history });
13445
13498
  const result = await chat.sendMessageStream(lastMessage);
13499
+ let streamStopReason;
13446
13500
  for await (const chunk of result.stream) {
13447
13501
  const text = chunk.text();
13448
13502
  if (text) {
13449
13503
  yield { type: "text", text };
13450
13504
  }
13505
+ const finishReason = chunk.candidates?.[0]?.finishReason;
13506
+ if (finishReason) {
13507
+ streamStopReason = this.mapFinishReason(finishReason);
13508
+ }
13451
13509
  }
13452
- yield { type: "done" };
13510
+ yield { type: "done", stopReason: streamStopReason };
13453
13511
  } catch (error) {
13454
13512
  throw this.handleError(error);
13455
13513
  }
@@ -13484,11 +13542,16 @@ var GeminiProvider = class {
13484
13542
  const chat = model.startChat({ history });
13485
13543
  const result = await chat.sendMessageStream(lastMessage);
13486
13544
  const emittedToolCalls = /* @__PURE__ */ new Set();
13545
+ let streamStopReason;
13487
13546
  for await (const chunk of result.stream) {
13488
13547
  const text = chunk.text();
13489
13548
  if (text) {
13490
13549
  yield { type: "text", text };
13491
13550
  }
13551
+ const finishReason = chunk.candidates?.[0]?.finishReason;
13552
+ if (finishReason) {
13553
+ streamStopReason = this.mapFinishReason(finishReason);
13554
+ }
13492
13555
  const candidate = chunk.candidates?.[0];
13493
13556
  if (candidate?.content?.parts) {
13494
13557
  for (const part of candidate.content.parts) {
@@ -13522,7 +13585,7 @@ var GeminiProvider = class {
13522
13585
  }
13523
13586
  }
13524
13587
  }
13525
- yield { type: "done" };
13588
+ yield { type: "done", stopReason: streamStopReason };
13526
13589
  } catch (error) {
13527
13590
  throw this.handleError(error);
13528
13591
  }