thaipass 0.1.0 → 0.1.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.
Files changed (2) hide show
  1. package/dist/index.js +31 -10
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -646,6 +646,11 @@ const fileFrameSchema = fileEventSchema.extend({
646
646
  data: fileEventSchema.optional(),
647
647
  type: z.literal("file")
648
648
  });
649
+ const toolErrorSchema = (type) => z.object({
650
+ errorText: optionalText,
651
+ toolName: optionalText,
652
+ type: z.literal(type)
653
+ });
649
654
  const upstreamEventSchema = z.discriminatedUnion("type", [
650
655
  z.object({
651
656
  delta: z.string(),
@@ -660,12 +665,23 @@ const upstreamEventSchema = z.discriminatedUnion("type", [
660
665
  finishReason: optionalText,
661
666
  type: z.literal("finish")
662
667
  }),
668
+ toolErrorSchema("tool-input-error"),
669
+ toolErrorSchema("tool-output-error"),
663
670
  z.object({
664
671
  error: optionalText,
665
672
  errorText: optionalText,
666
673
  type: z.literal("error")
667
674
  })
668
675
  ]);
676
+ const toFileEvent = (frame) => {
677
+ const { data } = frame;
678
+ return {
679
+ filename: frame.filename ?? data?.filename,
680
+ mediaType: frame.mediaType ?? data?.mediaType,
681
+ snapshotUrl: frame.snapshotUrl ?? data?.snapshotUrl,
682
+ url: frame.url ?? data?.url
683
+ };
684
+ };
669
685
  const skippedTypeSchema = z.object({ type: z.string() });
670
686
  const parseAipassSSE = async function* parseAipassSSE(body, skips) {
671
687
  const chunks = parseJsonEventStream({
@@ -695,25 +711,25 @@ const parseAipassSSE = async function* parseAipassSSE(body, skips) {
695
711
  text: event.delta
696
712
  };
697
713
  break;
698
- case "file": {
699
- const { data } = event;
714
+ case "file":
700
715
  yield {
701
- file: {
702
- filename: event.filename ?? data?.filename,
703
- mediaType: event.mediaType ?? data?.mediaType,
704
- snapshotUrl: event.snapshotUrl ?? data?.snapshotUrl,
705
- url: event.url ?? data?.url
706
- },
716
+ file: toFileEvent(event),
707
717
  kind: "file"
708
718
  };
709
719
  break;
710
- }
711
720
  case "finish":
712
721
  yield {
713
722
  kind: "finish",
714
723
  reason: event.finishReason ?? "stop"
715
724
  };
716
725
  break;
726
+ case "tool-input-error":
727
+ case "tool-output-error":
728
+ yield {
729
+ kind: "error",
730
+ message: `upstream failed on ${event.toolName ?? "a tool"}: ${event.errorText ?? "no detail"}`
731
+ };
732
+ break;
717
733
  default: yield {
718
734
  kind: "error",
719
735
  message: event.errorText ?? event.error ?? "upstream error"
@@ -928,7 +944,12 @@ const prepareTurn = async (conversation, model, thinking, catalog) => {
928
944
  thinkingLevel
929
945
  };
930
946
  };
931
- const finishReasonOf = (tally) => tally.calls > 0 && tally.finishReason === "stop" ? "tool-calls" : tally.finishReason;
947
+ /** Upstream promised a call and made none, so what it left is an apology. */
948
+ const isAbandonedToolCall = (tally) => tally.finishReason === "tool-calls" && tally.calls === 0;
949
+ const finishReasonOf = (tally) => {
950
+ if (isAbandonedToolCall(tally)) return "error";
951
+ return tally.calls > 0 && tally.finishReason === "stop" ? "tool-calls" : tally.finishReason;
952
+ };
932
953
  const replyTokens = (tally) => charTokens(tally.reply);
933
954
  /**
934
955
  * Text goes through the tool-call splitter, so a fenced call never reaches the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "thaipass",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "AI SDK provider for AI Pass, using your own session cookie.",
5
5
  "keywords": [
6
6
  "ai-sdk",