fast-completions 0.7.0 → 0.9.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/lib/fast/completions/errors.rb +31 -0
- data/lib/fast/completions/protocol.rb +20 -10
- data/lib/fast/completions/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca8f4d428afa218f2fdbec75c7f74206d3cb34d7a49234781ec1c1b4aba73cd1
|
|
4
|
+
data.tar.gz: 77bb70c53ed40fee638038da6996c39bd0d7231937fe4867f9e9e45a5c27a781
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 28c2471eef9b43aab6067de99a68a994453b1a63dd4fbc02568a9a8ce753e20349c1e66b1ac9289547a3a3223c1ae8e7278ea03881dbc19273de9a522b7d2dd0
|
|
7
|
+
data.tar.gz: b21faba437020f06cb2267e7bd219911880794c8f7b31d9311151c95811c26a1092dd8ac0d3cf4b92f5628b0602927b2d4d79b75000061e5238d7e5e13c205e1
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,39 @@
|
|
|
3
3
|
Notable changes to the fast-completions gem. The format is prose-first,
|
|
4
4
|
one section per released version, newest first.
|
|
5
5
|
|
|
6
|
+
## 0.9.0 (2026-11-15)
|
|
7
|
+
|
|
8
|
+
- Tool-args finalize honesty (I06): an arguments scratch that fails the
|
|
9
|
+
finalize whole-buffer parse — even after the control-character repair
|
|
10
|
+
walk — no longer degrades to a silent `arguments = {}` (pi's
|
|
11
|
+
partial-json last resort, which made a gateway double-emit look like
|
|
12
|
+
model incompetence: the muse-glimmer lineage). It now surfaces as the
|
|
13
|
+
loud in-band marker `{error: <message>, raw_arguments: <the raw
|
|
14
|
+
scratch>}` on the tool_call block's `:arguments` (and therefore on the
|
|
15
|
+
`:tool_call_end` event), constructed by
|
|
16
|
+
`Fast::Completions::ToolArgumentsFailure.marker(raw)` and detectable
|
|
17
|
+
with `ToolArgumentsFailure.marker?(value)`. The marker's keys are
|
|
18
|
+
SYMBOLS — `JSON.parse` only yields String keys — so it can never
|
|
19
|
+
collide with legitimate parsed arguments. Nothing raises:
|
|
20
|
+
`StreamAccumulator#finish` still returns, the stream outcome is
|
|
21
|
+
unchanged (a completed `tool_calls` stream still finalizes `[:done,
|
|
22
|
+
:tool_use]`), and consumers should turn the marker into an `{error:}`
|
|
23
|
+
tool result. Unchanged paths: an EMPTY scratch still finalizes `{}`
|
|
24
|
+
(the honest zero-arg / bytes-never-arrived case; the chunk-seam drop
|
|
25
|
+
path counts its own drops), a repairable control-char scratch still
|
|
26
|
+
repairs to valid arguments, and a well-formed stream is byte-for-byte
|
|
27
|
+
unchanged. The wire format is untouched. Consumers executing
|
|
28
|
+
arguments sight-unseen (tart pre-pin-bump) will now splat the marker's
|
|
29
|
+
symbol keys and fail loudly at the kwargs boundary instead of
|
|
30
|
+
executing a bogus zero-arg call — the intended honesty gain.
|
|
31
|
+
|
|
32
|
+
## 0.8.0 (2026-10-30)
|
|
33
|
+
|
|
34
|
+
- Ruby 3.3 support dropped: `required_ruby_version` moves from
|
|
35
|
+
`>= 3.3` to `>= 3.4`, and the monorepo CI matrix narrows to 3.4/4.0
|
|
36
|
+
to match. No other platform change — no new dependencies, no API
|
|
37
|
+
movement.
|
|
38
|
+
|
|
6
39
|
## 0.7.0 (2026-10-25)
|
|
7
40
|
|
|
8
41
|
- Telemetry sink interface: `Client.new` accepts `telemetry:` — any
|
|
@@ -59,6 +59,37 @@ module Fast
|
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# I06 serving-stack tool honesty: the loud in-band failure surface
|
|
63
|
+
# for tool-call arguments that fail the finalize parse even after the
|
|
64
|
+
# control-character repair walk (Protocol.parse_streaming_json). NEVER
|
|
65
|
+
# raised — a raise out of StreamAccumulator#finish would kill
|
|
66
|
+
# consuming agent sessions (the pre-tart-#19 crash lineage); the
|
|
67
|
+
# marker rides block[:arguments] itself, so every surface that
|
|
68
|
+
# already reads arguments (message blocks, :tool_call_end events)
|
|
69
|
+
# carries it with no new block keys. Collision-proof by construction:
|
|
70
|
+
# the marker's keys are SYMBOLS, and JSON.parse only ever yields
|
|
71
|
+
# String keys — no legitimate arguments hash can contain :error or
|
|
72
|
+
# :raw_arguments.
|
|
73
|
+
module ToolArgumentsFailure
|
|
74
|
+
MESSAGE = "tool call arguments are unparseable JSON at stream finalize " \
|
|
75
|
+
"(duplicated or truncated arguments stream); the raw argument " \
|
|
76
|
+
"bytes are carried in raw_arguments"
|
|
77
|
+
|
|
78
|
+
module_function
|
|
79
|
+
|
|
80
|
+
# The marker Hash: {error: MESSAGE, raw_arguments: raw}.
|
|
81
|
+
def marker(raw)
|
|
82
|
+
{error: MESSAGE, raw_arguments: raw}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# The consumer-side predicate (tart's chat.rb): true only for the
|
|
86
|
+
# marker — legitimate parsed arguments have String keys and can
|
|
87
|
+
# never match.
|
|
88
|
+
def marker?(value)
|
|
89
|
+
value.is_a?(Hash) && value.key?(:error) && value.key?(:raw_arguments)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
62
93
|
class CancelledError < StandardError; end # NOT < Error (D8)
|
|
63
94
|
|
|
64
95
|
# I63 constrained sampling (pi api/constrained-sampling.ts). INTERNAL:
|
|
@@ -212,10 +212,16 @@ module Fast
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
# OQ-4: control-character repair (pi utils/json-parse.ts repairJson
|
|
215
|
-
# :20-77)
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
#
|
|
215
|
+
# :20-77); I06: a NON-EMPTY scratch that still fails the parse no
|
|
216
|
+
# longer degrades to pi's silent {} partial-json fallback — it
|
|
217
|
+
# surfaces the loud in-band ToolArgumentsFailure marker instead (a
|
|
218
|
+
# parse failure must never masquerade as a deliberate zero-arg
|
|
219
|
+
# call). An EMPTY scratch stays {}: the honest representation of a
|
|
220
|
+
# genuine zero-arg call (or argument bytes that never arrived —
|
|
221
|
+
# the chunk-seam drop path counts those separately). The repaired
|
|
222
|
+
# re-parse gets its OWN method — a rescue body's exception is not
|
|
223
|
+
# caught by a later rescue clause of the same method (the 0.4.0
|
|
224
|
+
# leak: a delta boundary splitting an escape sequence left the
|
|
219
225
|
# scratch buffer mid-string with a trailing backslash; repair "fixed"
|
|
220
226
|
# it, the re-parse raised, and the step died). pi's outer try/catch
|
|
221
227
|
# absorbs this; so do we.
|
|
@@ -225,13 +231,15 @@ module Fast
|
|
|
225
231
|
JSON.parse(partial)
|
|
226
232
|
rescue JSON::ParserError
|
|
227
233
|
repaired = repair_json(partial)
|
|
228
|
-
repaired == partial ?
|
|
234
|
+
repaired == partial ? ToolArgumentsFailure.marker(partial) : parse_repaired_json(repaired, partial)
|
|
229
235
|
end
|
|
230
236
|
|
|
231
|
-
|
|
237
|
+
# `raw` is the ORIGINAL scratch: the marker reports the bytes the
|
|
238
|
+
# wire delivered, never our own repair transform of them.
|
|
239
|
+
def parse_repaired_json(repaired, raw)
|
|
232
240
|
JSON.parse(repaired)
|
|
233
241
|
rescue JSON::ParserError
|
|
234
|
-
|
|
242
|
+
ToolArgumentsFailure.marker(raw)
|
|
235
243
|
end
|
|
236
244
|
|
|
237
245
|
# Escape raw control characters inside strings; double backslashes
|
|
@@ -482,9 +490,11 @@ module Fast
|
|
|
482
490
|
raw = streamer ? streamer.scratch : "" # no-fragment blocks finalize an empty scratch
|
|
483
491
|
# Finalize is the SOLE authority (L9): one whole-buffer parse
|
|
484
492
|
# of the raw bytes — the JSON.parse fast path when the stream
|
|
485
|
-
# completed, the repair walk
|
|
486
|
-
#
|
|
487
|
-
#
|
|
493
|
+
# completed, the repair walk otherwise, and (I06) the loud
|
|
494
|
+
# in-band ToolArgumentsFailure marker when a NON-EMPTY
|
|
495
|
+
# scratch still will not parse — never pi's silent {}
|
|
496
|
+
# last resort. The live Hash the machine grew is replaced
|
|
497
|
+
# here, exactly once.
|
|
488
498
|
block[:arguments] = Protocol.parse_streaming_json(raw)
|
|
489
499
|
events << Event.new(type: :tool_call_end, content_index: index,
|
|
490
500
|
tool_call: {id: block[:id], name: block[:name], arguments: block[:arguments]},
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: fast-completions
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eric Jacobs
|
|
@@ -63,7 +63,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
63
63
|
requirements:
|
|
64
64
|
- - ">="
|
|
65
65
|
- !ruby/object:Gem::Version
|
|
66
|
-
version: '3.
|
|
66
|
+
version: '3.4'
|
|
67
67
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
68
|
requirements:
|
|
69
69
|
- - ">="
|