feat(client): DmService narrate loop + FakeDmTransport (all branches tested)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
27
client/scripts/net/dm_service.gd
Normal file
27
client/scripts/net/dm_service.gd
Normal file
@@ -0,0 +1,27 @@
|
||||
class_name DmService
|
||||
extends RefCounted
|
||||
## The client narrate loop (charter §2: the client owns the loop and consumes
|
||||
## text). Posts the canon log to /dm/narrate through an injected transport,
|
||||
## returns the prose to display and the [FACT:] tags to harvest. On ANY failure
|
||||
## it returns an authored fallback line — never an error. It harvests facts
|
||||
## only; the Narrator has no authority over disposition or moves, so those tags
|
||||
## are dropped even if the model emits them (§6). narrate() does not mutate the
|
||||
## log — the caller applies the returned facts, keeping the state write explicit.
|
||||
|
||||
var _transport: DmTransport
|
||||
var _fallback: FallbackLibrary
|
||||
|
||||
|
||||
func _init(transport: DmTransport, fallback: FallbackLibrary) -> void:
|
||||
_transport = transport
|
||||
_fallback = fallback
|
||||
|
||||
|
||||
func narrate(canon_log: CanonLog) -> NarrateResult:
|
||||
var resp: DmResponse = await _transport.post_json(
|
||||
"/dm/narrate", {"canon_log": canon_log.to_dict()})
|
||||
if not resp.transport_ok or resp.status != 200 \
|
||||
or typeof(resp.body) != TYPE_DICTIONARY or not resp.body.has("prose"):
|
||||
return NarrateResult.fallback(_fallback.narrator_line())
|
||||
var extracted := TagExtractor.extract(str(resp.body["prose"]))
|
||||
return NarrateResult.new(extracted["clean_text"], extracted["facts"], false)
|
||||
1
client/scripts/net/dm_service.gd.uid
Normal file
1
client/scripts/net/dm_service.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b467yu4cut0hp
|
||||
Reference in New Issue
Block a user