nsmcp::QuestionService method send (public)
<instance of nsmcp::QuestionService> send context question \ [ handle ] [ backendName ] [ browserTimezone ] [ peerAddress ] \ [ expectedTurn ] [ backendQuestion ] [ continuedFrom ]
Defined in /usr/local/ns/tcl/nsmcp/question-service.tcl
Queue a question or follow-up with optional browser timezone context. Retain authenticated ownership, backend selection and turn guards when queuing asynchronous provider work. Keep the displayed question separate from server-generated continuation instructions.
- Parameters:
- context (required)
- Authenticated request context carrying the principal, grants and selected server.
- question (required)
- Question text supplied to the selected provider.
- handle (optional)
- Existing conversation handle, or empty to create a new conversation.
- backendName (optional)
- Backend selection for a new conversation; empty retains the configured default.
- browserTimezone (optional)
- Validated browser timezone used unless the question overrides it; empty retains prior context or UTC.
- peerAddress (optional)
- Direct connection peer supplied by the authenticated HTTP adapter.
- expectedTurn (optional)
- Optional internal guard against continuing a superseded turn.
- backendQuestion (optional)
- Optional server-generated backend task, separate from the displayed question.
- continuedFrom (optional)
- Owned source-turn identifier when editing the original request.
- Returns:
- Rooted JSON triples describing the queued conversation.
- Testcases:
- No testcase defined.
Source code: if {$browserTimezone ne "" && (![regexp {^[A-Za-z0-9_+:/-]{1,128}$} $browserTimezone] || [catch { clock format 0 -timezone $browserTimezone }])} { error "Invalid browser timezone" } if {$peerAddress ne "" && ![ns_ip valid $peerAddress]} { error "Invalid question peer address" } set question [string trim $question] if {$question eq "" || [string bytelength $question] > 16384} { error "Supply a question up to 16 KiB" } if {$backendQuestion ne {} && ([string bytelength $backendQuestion]>16384 || $expectedTurn eq {} || $continuedFrom eq {})} { error "Invalid internal continuation task" } ns_mutex lock ${:mutex} try { if {$handle eq ""} { :cleanup foreach key [nsv_array names ${:store}] { set old [nsv_get ${:store} $key] if {[dict get $old expires] <= [clock seconds] && [dict get $old status] ni {queued running cancelling}} { :drop $key } } if {[llength [nsv_array names ${:store}]] >= ${:maxConversations}} { error "Conversation limit reached; remove an old conversation" } if {$backendName eq ""} { set backendName [:defaultBackend] } :provider $backendName set handle [ns_crypto::randombytes -encoding hex 32] set record [dict create principal [$context principal] server [$context server] backend $backendName status idle state {} turns {}] } else { set record [:owned $context $handle] if {![dict exists $record backend]} { dict set record backend [:defaultBackend] } if {$backendName ne "" && $backendName ne [dict get $record backend]} { error "Changing backend requires a new conversation" } } if {$backendQuestion ne {}} { set ownedSource false foreach entry [dict get $record turns] { if {[dict get $entry id] eq $continuedFrom} { set ownedSource true break } } if {!$ownedSource} { error "Continuation source is not in this conversation" } } if {$expectedTurn ne "" && [dict get $record current] ne $expectedTurn} { error "The conversation has moved on; refresh its status before continuing" } if {[dict get $record status] in {queued running cancelling}} { error "A question is already running in this conversation" } if {[llength [dict get $record turns]] >= ${:maxTurns}} { error "Conversation turn limit reached; start a new conversation" } if {$browserTimezone ne ""} { dict set record browserTimezone $browserTimezone } set turn [ns_crypto::randombytes -encoding hex 16] dict set record current $turn dict set record status queued dict set record expires [expr {[clock seconds]+int(${:lifetime})}] set description [[:provider [dict get $record backend]] describe] set model {} if {[dict exists $description model]} { set model [dict get $description model] } set last [dict create id $turn conversation $handle question $question answer {} status queued tools {} error {} peerAddress $peerAddress peerSource [expr {$peerAddress eq "" ? "unavailable" : "direct"}] submittedAt [clock seconds] submittedMs [clock milliseconds] requestedModel $model] if {$backendQuestion ne {}} { dict set last backendQuestion $backendQuestion dict set last continuedFrom $continuedFrom } if {[dict exists $description backend] && [dict get $description backend] eq "codex"} { dict set last questionUsage {source codex scope turn available false reason no-provider-usage} } dict lappend record turns $last :logQuestion submitted $record $last nsv_set ${:store} $handle $record try { ns_job queue -detached ${:queue} [list [self] run $handle $turn] } on error {message options} { dict set record status failed set last [lindex [dict get $record turns] end] dict set last status failed dict set last error "Unable to queue the question" dict set record turns [lreplace [dict get $record turns] end end $last] nsv_set ${:store} $handle $record :logQuestion finished $record $last return -options $options $message } } finally { ns_mutex unlock ${:mutex} } return [:poll $context $handle]XQL Not present: Generic, PostgreSQL, Oracle
![[i]](/resources/acs-subsite/ZoomIn16.gif)