@magpie-community/opencode-qoder-auth 0.2.15 → 0.2.16
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/README.md +6 -0
- package/index.mjs +62 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,6 +78,12 @@ itself.
|
|
|
78
78
|
reasoning and usage.
|
|
79
79
|
- Tool calls Qoder writes as XML or JSON in its text become tool calls.
|
|
80
80
|
Tool calls it sends the OpenAI way keep Qoder's id.
|
|
81
|
+
- A call written in the text that can't be read, or that the stream ends
|
|
82
|
+
inside, is never sent on as the answer (since 0.2.16, #132): the turn
|
|
83
|
+
ends as an error, a 502 or the stream's error frame with no finish, so
|
|
84
|
+
the agent or magpie can try again. A call whose JSON is whole when the
|
|
85
|
+
stream ends without its close is still the call; a tag in code or prose
|
|
86
|
+
stays text.
|
|
81
87
|
|
|
82
88
|
**Errors**
|
|
83
89
|
|
package/index.mjs
CHANGED
|
@@ -884,13 +884,37 @@ function parseCall(v) {
|
|
|
884
884
|
return { name: m[1].trim(), args: JSON.stringify(args) }
|
|
885
885
|
}
|
|
886
886
|
|
|
887
|
+
// quoted says text ends inside a code fence or an inline code span: a
|
|
888
|
+
// <tool_call> there is prose about one, not a call
|
|
889
|
+
function quoted(text) {
|
|
890
|
+
if ((text.match(/```/g)?.length ?? 0) % 2) return true
|
|
891
|
+
const line = text.slice(text.lastIndexOf("\n") + 1).replace(/```/g, "")
|
|
892
|
+
return (line.match(/`/g)?.length ?? 0) % 2 === 1
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
// callish says a block was on its way to being a call: its JSON began, or
|
|
896
|
+
// the XML function form
|
|
897
|
+
const callish = (v) => v.trimStart().startsWith("{") || v.includes("\x3cfunction=")
|
|
898
|
+
|
|
899
|
+
const CUT_SHORT = "the model's tool call, written as text, was cut short; it is not sent on as the answer"
|
|
900
|
+
const UNREADABLE = "the model's tool call, written as text, can't be read; it is not sent on as the answer"
|
|
901
|
+
|
|
887
902
|
// Splitter holds text until a whole tool-call block has come, then gives
|
|
888
|
-
// it as a call.
|
|
903
|
+
// it as a call. A block on its way to being a call that can't be read, or
|
|
904
|
+
// that the stream ended inside, is an error, never the answer (plugins#132,
|
|
905
|
+
// as trae's since plugins#38): the turn fails so it can be tried again. A
|
|
906
|
+
// block whose JSON is whole when the stream ends without its close is still
|
|
907
|
+
// the call; one that is no call at all, or one in code, stays text.
|
|
889
908
|
class Splitter {
|
|
890
909
|
textBuf = ""
|
|
891
910
|
callBuf = ""
|
|
892
911
|
inCall = false
|
|
893
912
|
sawTool = false
|
|
913
|
+
sent = "" // the text given out so far, to tell a block in code from a call
|
|
914
|
+
text(out, t) {
|
|
915
|
+
this.sent += t
|
|
916
|
+
out.push({ text: t })
|
|
917
|
+
}
|
|
894
918
|
feed(input) {
|
|
895
919
|
const out = []
|
|
896
920
|
while (input) {
|
|
@@ -905,11 +929,11 @@ class Splitter {
|
|
|
905
929
|
keep = n
|
|
906
930
|
break
|
|
907
931
|
}
|
|
908
|
-
if (keep < comb.length)
|
|
932
|
+
if (keep < comb.length) this.text(out, comb.slice(0, comb.length - keep))
|
|
909
933
|
this.textBuf = comb.slice(comb.length - keep)
|
|
910
934
|
break
|
|
911
935
|
}
|
|
912
|
-
if (i > 0)
|
|
936
|
+
if (i > 0) this.text(out, comb.slice(0, i))
|
|
913
937
|
input = comb.slice(i + CALL_OPEN.length)
|
|
914
938
|
this.inCall = true
|
|
915
939
|
continue
|
|
@@ -921,11 +945,16 @@ class Splitter {
|
|
|
921
945
|
this.callBuf = comb
|
|
922
946
|
break
|
|
923
947
|
}
|
|
924
|
-
const
|
|
948
|
+
const body = comb.slice(0, j)
|
|
949
|
+
const c = parseCall(body)
|
|
925
950
|
if (c) {
|
|
926
951
|
this.sawTool = true
|
|
927
952
|
out.push({ call: c })
|
|
928
|
-
} else
|
|
953
|
+
} else if (callish(body) && !quoted(this.sent)) {
|
|
954
|
+
this.inCall = false
|
|
955
|
+
out.push({ error: UNREADABLE })
|
|
956
|
+
return out
|
|
957
|
+
} else this.text(out, CALL_OPEN + body + CALL_CLOSE)
|
|
929
958
|
input = comb.slice(j + CALL_CLOSE.length)
|
|
930
959
|
this.inCall = false
|
|
931
960
|
}
|
|
@@ -933,14 +962,23 @@ class Splitter {
|
|
|
933
962
|
}
|
|
934
963
|
flush() {
|
|
935
964
|
if (this.inCall) {
|
|
936
|
-
const
|
|
965
|
+
const body = this.callBuf
|
|
937
966
|
this.inCall = false
|
|
938
967
|
this.callBuf = ""
|
|
968
|
+
if (callish(body) && !quoted(this.sent)) {
|
|
969
|
+
const c = parseCall(body)
|
|
970
|
+
if (!c) return [{ error: CUT_SHORT }]
|
|
971
|
+
this.sawTool = true
|
|
972
|
+
return [{ call: c }]
|
|
973
|
+
}
|
|
974
|
+
const t = CALL_OPEN + body
|
|
975
|
+
this.sent += t
|
|
939
976
|
return [{ text: t }]
|
|
940
977
|
}
|
|
941
978
|
if (this.textBuf) {
|
|
942
979
|
const t = this.textBuf
|
|
943
980
|
this.textBuf = ""
|
|
981
|
+
this.sent += t
|
|
944
982
|
return [{ text: t }]
|
|
945
983
|
}
|
|
946
984
|
return []
|
|
@@ -968,9 +1006,10 @@ async function* events(body) {
|
|
|
968
1006
|
const a = new Splitter()
|
|
969
1007
|
let index = -1 // the tool call being given
|
|
970
1008
|
let native = -1 // the native tool call being assembled
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1009
|
+
// one piece of the split text as an event: a call, text, or a call that
|
|
1010
|
+
// can't be read, which ends the turn as a 502
|
|
1011
|
+
const piece = (f) =>
|
|
1012
|
+
f.call ? { tool: { index: ++index, id: "call_" + hexID(), name: f.call.name, args: f.call.args } } : f.error ? { error: { status: 502, message: f.error } } : { text: f.text }
|
|
974
1013
|
let usage
|
|
975
1014
|
let fr = ""
|
|
976
1015
|
for await (const line of sseLines(body)) {
|
|
@@ -998,15 +1037,21 @@ async function* events(body) {
|
|
|
998
1037
|
if (delta.reasoning_content) yield { reasoning: delta.reasoning_content }
|
|
999
1038
|
if (typeof delta.content === "string")
|
|
1000
1039
|
for (const f of a.feed(delta.content)) {
|
|
1001
|
-
if (f.call
|
|
1002
|
-
|
|
1040
|
+
if (!f.call && !f.error && !f.text) continue
|
|
1041
|
+
const e = piece(f)
|
|
1042
|
+
yield e
|
|
1043
|
+
if (e.error) return
|
|
1003
1044
|
}
|
|
1004
1045
|
// tool calls served the OpenAI way keep Qoder's id: a result answers it
|
|
1005
1046
|
for (const tc of delta.tool_calls ?? []) {
|
|
1006
1047
|
const i = Number(tc.index ?? 0)
|
|
1007
1048
|
if (i !== native) {
|
|
1008
1049
|
native = i
|
|
1009
|
-
|
|
1050
|
+
for (const f of a.flush()) {
|
|
1051
|
+
const e = piece(f)
|
|
1052
|
+
yield e
|
|
1053
|
+
if (e.error) return
|
|
1054
|
+
}
|
|
1010
1055
|
a.sawTool = true
|
|
1011
1056
|
yield { tool: { index: ++index, id: tc.id || "call_" + hexID(), name: tc.function?.name ?? "", args: tc.function?.arguments ?? "" } }
|
|
1012
1057
|
} else if (tc.function?.arguments) yield { args: { index, text: tc.function.arguments } }
|
|
@@ -1025,7 +1070,11 @@ async function* events(body) {
|
|
|
1025
1070
|
...(u.completion_tokens_details?.reasoning_tokens ? { completion_tokens_details: { reasoning_tokens: u.completion_tokens_details.reasoning_tokens } } : {}),
|
|
1026
1071
|
}
|
|
1027
1072
|
}
|
|
1028
|
-
|
|
1073
|
+
for (const f of a.flush()) {
|
|
1074
|
+
const e = piece(f)
|
|
1075
|
+
yield e
|
|
1076
|
+
if (e.error) return
|
|
1077
|
+
}
|
|
1029
1078
|
yield { stop: a.sawTool ? "tool_calls" : fr === "length" ? "length" : "stop", usage }
|
|
1030
1079
|
}
|
|
1031
1080
|
|
package/package.json
CHANGED