thaipass 0.1.2 → 0.1.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.
- package/dist/index.js +68 -6
- package/package.json +2 -3
package/dist/index.js
CHANGED
|
@@ -77,6 +77,7 @@ const ANY_MODELS = [...CHAT_MODELS, ...MEDIA_MODELS];
|
|
|
77
77
|
z.enum(ANY_MODELS, { error: "unknown model, see GET /v1/models" });
|
|
78
78
|
const current = {
|
|
79
79
|
origin: "https://de.aipass.net",
|
|
80
|
+
pricesUrl: "https://openrouter.ai/api/v1/models",
|
|
80
81
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36"
|
|
81
82
|
};
|
|
82
83
|
const config = current;
|
|
@@ -724,12 +725,15 @@ const parseAipassSSE = async function* parseAipassSSE(body, skips) {
|
|
|
724
725
|
};
|
|
725
726
|
break;
|
|
726
727
|
case "tool-input-error":
|
|
727
|
-
case "tool-output-error":
|
|
728
|
+
case "tool-output-error": {
|
|
729
|
+
const tool = event.toolName ?? "a tool";
|
|
728
730
|
yield {
|
|
729
731
|
kind: "error",
|
|
730
|
-
message: `upstream failed on ${
|
|
732
|
+
message: `upstream failed on ${tool}: ${event.errorText ?? "no detail"}`,
|
|
733
|
+
tool
|
|
731
734
|
};
|
|
732
735
|
break;
|
|
736
|
+
}
|
|
733
737
|
default: yield {
|
|
734
738
|
kind: "error",
|
|
735
739
|
message: event.errorText ?? event.error ?? "upstream error"
|
|
@@ -815,9 +819,47 @@ const heldLength = (tail) => {
|
|
|
815
819
|
for (let length = longest; length > 0; length -= 1) if ("```tool_call\n".startsWith(tail.slice(-length))) return length;
|
|
816
820
|
return 0;
|
|
817
821
|
};
|
|
822
|
+
const UNFENCED_LIMIT = 4096;
|
|
823
|
+
const objectEnd = (value, from) => {
|
|
824
|
+
let depth = 0;
|
|
825
|
+
let inString = false;
|
|
826
|
+
let escaped = false;
|
|
827
|
+
for (let index = from; index < value.length; index += 1) {
|
|
828
|
+
const char = value[index];
|
|
829
|
+
if (inString) {
|
|
830
|
+
if (escaped) escaped = false;
|
|
831
|
+
else if (char === "\\") escaped = true;
|
|
832
|
+
else if (char === "\"") inString = false;
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
if (char === "\"") inString = true;
|
|
836
|
+
else if (char === "{") depth += 1;
|
|
837
|
+
else if (char === "}") {
|
|
838
|
+
depth -= 1;
|
|
839
|
+
if (depth === 0) return index + 1;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return -1;
|
|
843
|
+
};
|
|
818
844
|
/**
|
|
819
845
|
* Text is released as soon as it can no longer open a fence. A fence that
|
|
820
846
|
* never closes, or holds no call to an offered tool, is released as text.
|
|
847
|
+
*
|
|
848
|
+
* A reply that opens with the call object and no fence is read as a call
|
|
849
|
+
* anyway. Some models write the body the guide asks for and drop the fence
|
|
850
|
+
* around it, and the reply then arrives as prose with `stop`: observed as
|
|
851
|
+
* `{"name":"load_skill","input":{…}}I can begin once the load_skill tool
|
|
852
|
+
* result is available.` The caller sees a finished answer that narrates a
|
|
853
|
+
* call nobody ran, which is worse than an error, and the retry in
|
|
854
|
+
* `reply.ts` cannot see it either, since that waits for a `tool-calls`
|
|
855
|
+
* finish reason. Recovering it here serves a streaming caller too, which
|
|
856
|
+
* cannot be retried at all.
|
|
857
|
+
*
|
|
858
|
+
* The rule is deliberately the narrowest one that covers what was observed:
|
|
859
|
+
* the object must be the first thing in the reply, it must parse, and it
|
|
860
|
+
* must name a tool the caller offered — the same bar a fenced block passes.
|
|
861
|
+
* Prose that quotes a call later on is left alone, which matters when the
|
|
862
|
+
* caller is reviewing code and may quote one on purpose.
|
|
821
863
|
*/
|
|
822
864
|
const splitReply = (tools) => {
|
|
823
865
|
if (tools.length === 0) return {
|
|
@@ -827,6 +869,7 @@ const splitReply = (tools) => {
|
|
|
827
869
|
const names = new Set(tools.map((tool) => tool.name));
|
|
828
870
|
let buffer = "";
|
|
829
871
|
let inBlock = false;
|
|
872
|
+
let opening = true;
|
|
830
873
|
/** Ends the open block; `fence` is what closed it, nothing when the reply ran out first. */
|
|
831
874
|
const closeBlock = (json, fence) => {
|
|
832
875
|
inBlock = false;
|
|
@@ -836,9 +879,31 @@ const splitReply = (tools) => {
|
|
|
836
879
|
type: "call"
|
|
837
880
|
}] : text(`${FENCE_OPEN}${json}${fence}`);
|
|
838
881
|
};
|
|
882
|
+
const openingCall = (final) => {
|
|
883
|
+
const start = buffer.length - buffer.trimStart().length;
|
|
884
|
+
if (buffer[start] !== "{") return buffer.trim().length === 0 && !final ? "hold" : void 0;
|
|
885
|
+
const end = objectEnd(buffer, start);
|
|
886
|
+
if (end === -1) return !final && buffer.length - start < UNFENCED_LIMIT ? "hold" : void 0;
|
|
887
|
+
const call = parseCall(buffer.slice(start, end), names);
|
|
888
|
+
if (call === void 0) return;
|
|
889
|
+
buffer = buffer.slice(end);
|
|
890
|
+
return call;
|
|
891
|
+
};
|
|
839
892
|
const drain = (final) => {
|
|
840
893
|
const parts = [];
|
|
841
894
|
for (;;) {
|
|
895
|
+
if (opening && !inBlock) {
|
|
896
|
+
const found = openingCall(final);
|
|
897
|
+
if (found === "hold") return parts;
|
|
898
|
+
opening = false;
|
|
899
|
+
if (found !== void 0) {
|
|
900
|
+
parts.push({
|
|
901
|
+
call: found,
|
|
902
|
+
type: "call"
|
|
903
|
+
});
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
842
907
|
if (inBlock) {
|
|
843
908
|
const end = buffer.indexOf(FENCE_CLOSE);
|
|
844
909
|
if (end === -1) {
|
|
@@ -1013,10 +1078,7 @@ const readReply = (options) => {
|
|
|
1013
1078
|
};
|
|
1014
1079
|
}
|
|
1015
1080
|
} else if (event.kind === "finish") tally.finishReason = event.reason;
|
|
1016
|
-
else yield
|
|
1017
|
-
kind: "error",
|
|
1018
|
-
message: event.message
|
|
1019
|
-
};
|
|
1081
|
+
else yield event;
|
|
1020
1082
|
yield* fromParts(splitter.flush());
|
|
1021
1083
|
}(),
|
|
1022
1084
|
tally
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thaipass",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "AI SDK provider for AI Pass, using your own session cookie.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-sdk",
|
|
@@ -42,8 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@thaipass/core": "workspace:*",
|
|
45
|
-
"@
|
|
46
|
-
"@types/bun": "1.4.0",
|
|
45
|
+
"@types/bun": "1.4.1",
|
|
47
46
|
"tsdown": "0.23.0",
|
|
48
47
|
"typescript": "7.0.2"
|
|
49
48
|
}
|