@lovelace_lol/polymer 0.0.1
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 +226 -0
- package/dist/index.d.ts +2603 -0
- package/dist/index.js +1031 -0
- package/dist/test.cjs +1519 -0
- package/dist/worker/agency-worker.js +972 -0
- package/dist/worker/manifest.edn +1 -0
- package/package.json +57 -0
- package/shadow-cljs.edn +173 -0
- package/src/polymer/animation/agency.cljs +84 -0
- package/src/polymer/animation/domain.cljs +110 -0
- package/src/polymer/animation/planner.cljs +88 -0
- package/src/polymer/animation/runtime.cljs +137 -0
- package/src/polymer/animation/scheduler.cljs +178 -0
- package/src/polymer/animation/service.cljs +981 -0
- package/src/polymer/animation/state.cljs +112 -0
- package/src/polymer/blink/agency.cljs +131 -0
- package/src/polymer/blink/planner.cljs +115 -0
- package/src/polymer/blink/scheduler.cljs +90 -0
- package/src/polymer/blink/snippet.cljs +50 -0
- package/src/polymer/blink/state.cljs +102 -0
- package/src/polymer/camera_context/agency.cljs +132 -0
- package/src/polymer/camera_context/domain.cljs +186 -0
- package/src/polymer/camera_context/planner.cljs +50 -0
- package/src/polymer/camera_context/scheduler.cljs +113 -0
- package/src/polymer/camera_context/state.cljs +121 -0
- package/src/polymer/character.cljs +471 -0
- package/src/polymer/conversation/agency.cljs +83 -0
- package/src/polymer/conversation/domain.cljs +35 -0
- package/src/polymer/conversation/effector.cljs +130 -0
- package/src/polymer/conversation/planner.cljs +178 -0
- package/src/polymer/conversation/scheduler.cljs +202 -0
- package/src/polymer/conversation/service.cljs +329 -0
- package/src/polymer/conversation/state.cljs +186 -0
- package/src/polymer/core.cljs +172 -0
- package/src/polymer/eye_head/agency.cljs +75 -0
- package/src/polymer/eye_head/domain.cljs +133 -0
- package/src/polymer/eye_head/goap.cljs +106 -0
- package/src/polymer/eye_head/scheduler.cljs +176 -0
- package/src/polymer/eye_head/service.cljs +259 -0
- package/src/polymer/eye_head/snippet.cljs +92 -0
- package/src/polymer/eye_head/state.cljs +167 -0
- package/src/polymer/gaze/agency.cljs +85 -0
- package/src/polymer/gaze/domain.cljs +211 -0
- package/src/polymer/gaze/goap.cljs +208 -0
- package/src/polymer/gaze/scheduler.cljs +184 -0
- package/src/polymer/gaze/state.cljs +202 -0
- package/src/polymer/gesture/agency.cljs +185 -0
- package/src/polymer/gesture/goap.cljs +461 -0
- package/src/polymer/gesture/scheduler.cljs +122 -0
- package/src/polymer/gesture/snippet.cljs +198 -0
- package/src/polymer/gesture/state.cljs +273 -0
- package/src/polymer/lipsync/agency.cljs +593 -0
- package/src/polymer/lipsync/articulation/jaw.cljs +258 -0
- package/src/polymer/lipsync/articulation/snippet.cljs +605 -0
- package/src/polymer/lipsync/articulation/tongue.cljs +418 -0
- package/src/polymer/lipsync/articulation/visemes.cljs +429 -0
- package/src/polymer/lipsync/goap.cljs +132 -0
- package/src/polymer/lipsync/pronunciation/effector.cljs +51 -0
- package/src/polymer/lipsync/scheduler.cljs +302 -0
- package/src/polymer/lipsync/state.cljs +202 -0
- package/src/polymer/nlp/phonemes.cljs +164 -0
- package/src/polymer/prosodic/agency.cljs +196 -0
- package/src/polymer/prosodic/goap.cljs +122 -0
- package/src/polymer/prosodic/scheduler.cljs +99 -0
- package/src/polymer/prosodic/snippet.cljs +106 -0
- package/src/polymer/prosodic/state.cljs +139 -0
- package/src/polymer/runtime/embody.cljs +117 -0
- package/src/polymer/runtime/embody_wasm.cljs +177 -0
- package/src/polymer/runtime/rust_embody_host.cljs +645 -0
- package/src/polymer/society.cljs +239 -0
- package/src/polymer/stream.cljs +130 -0
- package/src/polymer/transcription/agency.cljs +69 -0
- package/src/polymer/transcription/domain.cljs +68 -0
- package/src/polymer/transcription/effector.cljs +611 -0
- package/src/polymer/transcription/planner.cljs +203 -0
- package/src/polymer/transcription/scheduler.cljs +225 -0
- package/src/polymer/transcription/service.cljs +209 -0
- package/src/polymer/transcription/state.cljs +239 -0
- package/src/polymer/tts/agency.cljs +503 -0
- package/src/polymer/tts/azure.cljs +290 -0
- package/src/polymer/tts/planner.cljs +178 -0
- package/src/polymer/tts/runtime.cljs +624 -0
- package/src/polymer/tts/scheduler.cljs +162 -0
- package/src/polymer/tts/state.cljs +191 -0
- package/src/polymer/worker.cljs +70 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
(ns polymer.conversation.effector
|
|
2
|
+
(:require [polymer.conversation.domain :as domain]))
|
|
3
|
+
|
|
4
|
+
;; The provider effector is the only Conversation code allowed to invoke an
|
|
5
|
+
;; external LLM/backend provider. It receives already-planned request data from
|
|
6
|
+
;; the scheduler and reports plain completion data back to the agency.
|
|
7
|
+
|
|
8
|
+
(defn provider-call
|
|
9
|
+
[provider]
|
|
10
|
+
(cond
|
|
11
|
+
(fn? provider)
|
|
12
|
+
{:target nil
|
|
13
|
+
:respond provider}
|
|
14
|
+
|
|
15
|
+
(and provider (fn? (aget provider "respond")))
|
|
16
|
+
{:target provider
|
|
17
|
+
:respond (aget provider "respond")}
|
|
18
|
+
|
|
19
|
+
:else nil))
|
|
20
|
+
|
|
21
|
+
(defn thenable?
|
|
22
|
+
[value]
|
|
23
|
+
(and value (fn? (aget value "then"))))
|
|
24
|
+
|
|
25
|
+
(defn cleanup-callback
|
|
26
|
+
[value]
|
|
27
|
+
(cond
|
|
28
|
+
(fn? value)
|
|
29
|
+
value
|
|
30
|
+
|
|
31
|
+
:else
|
|
32
|
+
(when value
|
|
33
|
+
(some (fn [method-name]
|
|
34
|
+
(when-let [method (aget value method-name)]
|
|
35
|
+
(when (fn? method)
|
|
36
|
+
#(.call method value))))
|
|
37
|
+
["cancel" "abort" "stop" "dispose"]))))
|
|
38
|
+
|
|
39
|
+
(defn response-data
|
|
40
|
+
[response]
|
|
41
|
+
(cond
|
|
42
|
+
(string? response) {:text response}
|
|
43
|
+
(map? response) response
|
|
44
|
+
response (domain/data-map response)
|
|
45
|
+
:else {}))
|
|
46
|
+
|
|
47
|
+
(defn error-message
|
|
48
|
+
[error]
|
|
49
|
+
(or (domain/clean-text (when error (aget error "message")))
|
|
50
|
+
(domain/clean-text (when (string? error) error))
|
|
51
|
+
(domain/clean-text (str error))
|
|
52
|
+
"conversation-provider-error"))
|
|
53
|
+
|
|
54
|
+
(defn create-provider-effector
|
|
55
|
+
[provider]
|
|
56
|
+
(let [call (provider-call provider)
|
|
57
|
+
active (atom {})
|
|
58
|
+
disposed? (atom false)]
|
|
59
|
+
(letfn [(active-token? [request-id token]
|
|
60
|
+
(identical? token (get-in @active [request-id :token])))
|
|
61
|
+
|
|
62
|
+
(settle! [request-id token callback value]
|
|
63
|
+
(when (and (not @disposed?)
|
|
64
|
+
(active-token? request-id token))
|
|
65
|
+
(swap! active dissoc request-id)
|
|
66
|
+
(callback value)))
|
|
67
|
+
|
|
68
|
+
(cancel-entry! [request-id]
|
|
69
|
+
(when-let [{:keys [cleanup]} (get @active request-id)]
|
|
70
|
+
(swap! active dissoc request-id)
|
|
71
|
+
(when cleanup
|
|
72
|
+
(try
|
|
73
|
+
(cleanup)
|
|
74
|
+
(catch :default _
|
|
75
|
+
nil)))))
|
|
76
|
+
|
|
77
|
+
(remember-cleanup! [request-id token cleanup]
|
|
78
|
+
(when (and cleanup (active-token? request-id token))
|
|
79
|
+
(swap! active assoc-in [request-id :cleanup] cleanup)))
|
|
80
|
+
|
|
81
|
+
(invoke! [request on-success on-error]
|
|
82
|
+
(when (and call (not @disposed?))
|
|
83
|
+
(let [request-id (:requestId request)
|
|
84
|
+
token (js-obj)
|
|
85
|
+
succeed! #(settle! request-id token on-success
|
|
86
|
+
(response-data %))
|
|
87
|
+
fail! #(settle! request-id token on-error
|
|
88
|
+
{:message (error-message %)})
|
|
89
|
+
request-js (clj->js (dissoc request
|
|
90
|
+
:type
|
|
91
|
+
:agency
|
|
92
|
+
:targetAgency))]
|
|
93
|
+
(cancel-entry! request-id)
|
|
94
|
+
(swap! active assoc request-id {:token token
|
|
95
|
+
:cleanup nil})
|
|
96
|
+
(try
|
|
97
|
+
(let [result (.call (:respond call)
|
|
98
|
+
(:target call)
|
|
99
|
+
request-js
|
|
100
|
+
succeed!
|
|
101
|
+
fail!)
|
|
102
|
+
cleanup (cleanup-callback result)]
|
|
103
|
+
(cond
|
|
104
|
+
(thenable? result)
|
|
105
|
+
(do
|
|
106
|
+
(remember-cleanup! request-id token cleanup)
|
|
107
|
+
(.then result succeed! fail!))
|
|
108
|
+
|
|
109
|
+
cleanup
|
|
110
|
+
(remember-cleanup! request-id token cleanup)
|
|
111
|
+
|
|
112
|
+
(some? result)
|
|
113
|
+
(succeed! result)
|
|
114
|
+
|
|
115
|
+
:else nil))
|
|
116
|
+
(catch :default error
|
|
117
|
+
(fail! error))))))
|
|
118
|
+
|
|
119
|
+
(cancel-all! []
|
|
120
|
+
(doseq [request-id (keys @active)]
|
|
121
|
+
(cancel-entry! request-id)))]
|
|
122
|
+
{:available? (boolean call)
|
|
123
|
+
:invoke invoke!
|
|
124
|
+
:cancel cancel-entry!
|
|
125
|
+
:cancel-all cancel-all!
|
|
126
|
+
:dispose
|
|
127
|
+
(fn []
|
|
128
|
+
(when-not @disposed?
|
|
129
|
+
(cancel-all!)
|
|
130
|
+
(reset! disposed? true)))})))
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
(ns polymer.conversation.planner
|
|
2
|
+
(:require [polymer.conversation.domain :as domain]))
|
|
3
|
+
|
|
4
|
+
;; Conversation planning stays agency-local. It decides what this agency wants
|
|
5
|
+
;; to do with a transcript, provider result, or interruption, then hands ordered
|
|
6
|
+
;; action data to the scheduler. It never calls a backend, TTS provider, or peer
|
|
7
|
+
;; agency function directly.
|
|
8
|
+
|
|
9
|
+
(def supported-command-types
|
|
10
|
+
#{"configure"
|
|
11
|
+
"start"
|
|
12
|
+
"stop"
|
|
13
|
+
"reset"
|
|
14
|
+
"cancel"
|
|
15
|
+
"interrupt"
|
|
16
|
+
"transcript.final"
|
|
17
|
+
"transcriptFinal"
|
|
18
|
+
"userUtterance"
|
|
19
|
+
"agentUtterance"
|
|
20
|
+
"responseReady"
|
|
21
|
+
"providerError"
|
|
22
|
+
"tts.status"})
|
|
23
|
+
|
|
24
|
+
(defn command-type
|
|
25
|
+
[command]
|
|
26
|
+
(:type command))
|
|
27
|
+
|
|
28
|
+
(defn request-id
|
|
29
|
+
[prefix now-ms]
|
|
30
|
+
(str prefix ":" now-ms ":" (rand-int 1000000)))
|
|
31
|
+
|
|
32
|
+
(defn stale-response?
|
|
33
|
+
[command state]
|
|
34
|
+
(or (:interrupted state)
|
|
35
|
+
(nil? (:pendingResponse state))
|
|
36
|
+
(and (:turnId command)
|
|
37
|
+
(not= (:turnId command) (:turnId state)))
|
|
38
|
+
(and (:requestId command)
|
|
39
|
+
(not= (:requestId command)
|
|
40
|
+
(:requestId (:pendingResponse state))))))
|
|
41
|
+
|
|
42
|
+
(defn failure-step
|
|
43
|
+
[command]
|
|
44
|
+
(cond
|
|
45
|
+
(not (contains? supported-command-types (command-type command)))
|
|
46
|
+
{:op "fail"
|
|
47
|
+
:reason "unsupported-command"
|
|
48
|
+
:commandType (command-type command)}
|
|
49
|
+
|
|
50
|
+
(and (#{"transcript.final" "transcriptFinal" "userUtterance"} (command-type command))
|
|
51
|
+
(not (domain/user-text command)))
|
|
52
|
+
{:op "fail"
|
|
53
|
+
:reason "missing-user-text"
|
|
54
|
+
:commandType (command-type command)}
|
|
55
|
+
|
|
56
|
+
(and (#{"agentUtterance" "responseReady"} (command-type command))
|
|
57
|
+
(not (domain/agent-text command)))
|
|
58
|
+
{:op "fail"
|
|
59
|
+
:reason "missing-agent-text"
|
|
60
|
+
:commandType (command-type command)}))
|
|
61
|
+
|
|
62
|
+
(defn transcript-steps
|
|
63
|
+
[command state now-ms]
|
|
64
|
+
(let [text (domain/user-text command)
|
|
65
|
+
source (or (:source command) "transcription")
|
|
66
|
+
pending-request-id (get-in state [:pendingResponse :requestId])
|
|
67
|
+
response-text (domain/clean-text (or (:responseText command) (:agentText command)))
|
|
68
|
+
response-request {:requestId (request-id "conversation:response" now-ms)
|
|
69
|
+
:text text
|
|
70
|
+
:source source
|
|
71
|
+
:turnId (:turnId state)
|
|
72
|
+
:history (:history state)
|
|
73
|
+
:requestedAt now-ms}]
|
|
74
|
+
(cond-> [{:op "record-user-utterance"
|
|
75
|
+
:text text
|
|
76
|
+
:source source}
|
|
77
|
+
{:op "emit-user-utterance"
|
|
78
|
+
:text text
|
|
79
|
+
:source source}]
|
|
80
|
+
(and (get-in state [:config :autoRespond])
|
|
81
|
+
pending-request-id)
|
|
82
|
+
(conj {:op "cancel-provider"
|
|
83
|
+
:requestId pending-request-id
|
|
84
|
+
:reason "superseded"})
|
|
85
|
+
|
|
86
|
+
(get-in state [:config :autoRespond])
|
|
87
|
+
(conj {:op "request-response"
|
|
88
|
+
:request response-request}
|
|
89
|
+
{:op "invoke-provider"
|
|
90
|
+
:request response-request})
|
|
91
|
+
|
|
92
|
+
response-text
|
|
93
|
+
(conj {:op "record-agent-utterance"
|
|
94
|
+
:text response-text
|
|
95
|
+
:source "response"}
|
|
96
|
+
{:op "request-tts"
|
|
97
|
+
:text response-text
|
|
98
|
+
:source "response"
|
|
99
|
+
:requestId (request-id "conversation:tts" now-ms)}))))
|
|
100
|
+
|
|
101
|
+
(defn agent-utterance-steps
|
|
102
|
+
[command state now-ms]
|
|
103
|
+
(let [text (domain/agent-text command)]
|
|
104
|
+
(if (and (= "responseReady" (:type command))
|
|
105
|
+
(stale-response? command state))
|
|
106
|
+
[{:op "ignore-stale-response"
|
|
107
|
+
:reason "stale-response"
|
|
108
|
+
:requestId (:requestId command)
|
|
109
|
+
:turnId (:turnId command)}]
|
|
110
|
+
[{:op "record-agent-utterance"
|
|
111
|
+
:text text
|
|
112
|
+
:source (or (:source command) "conversation")}
|
|
113
|
+
{:op "request-tts"
|
|
114
|
+
:text text
|
|
115
|
+
:source (or (:source command) "conversation")
|
|
116
|
+
:requestId (request-id "conversation:tts" now-ms)}])))
|
|
117
|
+
|
|
118
|
+
(defn provider-error-steps
|
|
119
|
+
[command state]
|
|
120
|
+
(if (stale-response? command state)
|
|
121
|
+
[{:op "ignore-stale-response"
|
|
122
|
+
:reason "stale-provider-error"
|
|
123
|
+
:requestId (:requestId command)
|
|
124
|
+
:turnId (:turnId command)}]
|
|
125
|
+
[{:op "record-provider-error"
|
|
126
|
+
:requestId (:requestId command)
|
|
127
|
+
:turnId (:turnId command)
|
|
128
|
+
:message (or (:message command) "conversation-provider-error")}
|
|
129
|
+
{:op "publish-provider-error"
|
|
130
|
+
:requestId (:requestId command)
|
|
131
|
+
:turnId (:turnId command)
|
|
132
|
+
:message (or (:message command) "conversation-provider-error")}]))
|
|
133
|
+
|
|
134
|
+
(defn command-steps
|
|
135
|
+
[command state now-ms]
|
|
136
|
+
(if-let [failure (failure-step command)]
|
|
137
|
+
[failure]
|
|
138
|
+
(case (command-type command)
|
|
139
|
+
"configure" [{:op "apply-config"}
|
|
140
|
+
{:op "publish-status" :status (:status state)}]
|
|
141
|
+
"start" [{:op "start-session"}
|
|
142
|
+
{:op "publish-status" :status "listening"}]
|
|
143
|
+
"stop" [{:op "cancel-provider"
|
|
144
|
+
:requestId (get-in state [:pendingResponse :requestId])
|
|
145
|
+
:reason (or (:reason command) "stopped")}
|
|
146
|
+
{:op "stop-session" :reason (or (:reason command) "stopped")}
|
|
147
|
+
{:op "publish-status" :status "idle"}]
|
|
148
|
+
"reset" [{:op "cancel-provider"
|
|
149
|
+
:requestId (get-in state [:pendingResponse :requestId])
|
|
150
|
+
:reason "reset"}
|
|
151
|
+
{:op "reset-state"}
|
|
152
|
+
{:op "publish-status" :status "idle"}]
|
|
153
|
+
("cancel" "interrupt") [{:op "cancel-provider"
|
|
154
|
+
:requestId (get-in state [:pendingResponse :requestId])
|
|
155
|
+
:reason (or (:reason command) (command-type command))}
|
|
156
|
+
{:op "cancel-turn"
|
|
157
|
+
:reason (or (:reason command) (command-type command))}
|
|
158
|
+
{:op "publish-cancel"
|
|
159
|
+
:reason (or (:reason command) (command-type command))}
|
|
160
|
+
{:op "publish-status" :status "interrupted"}]
|
|
161
|
+
("transcript.final" "transcriptFinal" "userUtterance")
|
|
162
|
+
(transcript-steps command state now-ms)
|
|
163
|
+
("agentUtterance" "responseReady")
|
|
164
|
+
(agent-utterance-steps command state now-ms)
|
|
165
|
+
"providerError"
|
|
166
|
+
(provider-error-steps command state)
|
|
167
|
+
"tts.status" [{:op "record-tts-status"
|
|
168
|
+
:status (:status command)}
|
|
169
|
+
{:op "publish-status" :status (:status command)}])))
|
|
170
|
+
|
|
171
|
+
(defn plan-command
|
|
172
|
+
[command state now-ms]
|
|
173
|
+
(let [steps (command-steps command state now-ms)]
|
|
174
|
+
{:agency "conversation"
|
|
175
|
+
:commandType (command-type command)
|
|
176
|
+
:createdAt now-ms
|
|
177
|
+
:ok (not= "fail" (:op (first steps)))
|
|
178
|
+
:steps steps}))
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
(ns polymer.conversation.scheduler
|
|
2
|
+
(:require [polymer.conversation.state :as state]))
|
|
3
|
+
|
|
4
|
+
;; The Conversation scheduler is the ordered work boundary for the agency. This
|
|
5
|
+
;; first slice executes immediately, but it still records every planned step in
|
|
6
|
+
;; a queue so future backend/provider sequencing, retries, and cancellation can
|
|
7
|
+
;; extend the same architecture without moving logic into host UI code.
|
|
8
|
+
|
|
9
|
+
(defn now-ms
|
|
10
|
+
[]
|
|
11
|
+
(.now js/Date))
|
|
12
|
+
|
|
13
|
+
(defn queue-entry
|
|
14
|
+
[step index plan]
|
|
15
|
+
(assoc step
|
|
16
|
+
:agency "conversation"
|
|
17
|
+
:queueIndex index
|
|
18
|
+
:commandType (:commandType plan)
|
|
19
|
+
:queuedAt (now-ms)))
|
|
20
|
+
|
|
21
|
+
(defn emit-status!
|
|
22
|
+
[emit-event state-atom status reason]
|
|
23
|
+
(emit-event (cond-> {:type "conversation.status"
|
|
24
|
+
:agency "conversation"
|
|
25
|
+
:status status
|
|
26
|
+
:turnId (:turnId @state-atom)
|
|
27
|
+
:at (now-ms)}
|
|
28
|
+
reason (assoc :reason reason))))
|
|
29
|
+
|
|
30
|
+
(defn create-scheduler
|
|
31
|
+
[{:keys [state-atom emit-event dispatch-command provider-effector]}]
|
|
32
|
+
(let [queue (atom [])
|
|
33
|
+
disposed? (atom false)]
|
|
34
|
+
(letfn [(enqueue! [step plan]
|
|
35
|
+
(let [entry (queue-entry step (count @queue) plan)]
|
|
36
|
+
(swap! queue conj entry)
|
|
37
|
+
entry))
|
|
38
|
+
|
|
39
|
+
(request-tts! [step requested-at]
|
|
40
|
+
(let [request {:type "tts.requestSpeak"
|
|
41
|
+
:agency "conversation"
|
|
42
|
+
:targetAgency (get-in @state-atom [:config :ttsAgency])
|
|
43
|
+
:requestId (:requestId step)
|
|
44
|
+
:text (:text step)
|
|
45
|
+
:source (:source step)
|
|
46
|
+
:turnId (:turnId @state-atom)
|
|
47
|
+
:requestedAt requested-at
|
|
48
|
+
:command {:type "speak"
|
|
49
|
+
:text (:text step)
|
|
50
|
+
:source "conversation"}}]
|
|
51
|
+
(swap! state-atom state/record-tts-request request)
|
|
52
|
+
(emit-event request)))
|
|
53
|
+
|
|
54
|
+
(invoke-provider! [request]
|
|
55
|
+
(when (:available? provider-effector)
|
|
56
|
+
((:invoke provider-effector)
|
|
57
|
+
request
|
|
58
|
+
(fn [response]
|
|
59
|
+
(dispatch-command
|
|
60
|
+
{:type "responseReady"
|
|
61
|
+
:requestId (:requestId request)
|
|
62
|
+
:turnId (:turnId request)
|
|
63
|
+
:text (or (:text response)
|
|
64
|
+
(:responseText response)
|
|
65
|
+
(:agentText response))
|
|
66
|
+
:source (or (:source response)
|
|
67
|
+
(get-in @state-atom [:config :responseSource]))}))
|
|
68
|
+
(fn [error]
|
|
69
|
+
(dispatch-command
|
|
70
|
+
{:type "providerError"
|
|
71
|
+
:requestId (:requestId request)
|
|
72
|
+
:turnId (:turnId request)
|
|
73
|
+
:message (:message error)})))))
|
|
74
|
+
|
|
75
|
+
(execute-step! [step command plan]
|
|
76
|
+
(let [now (now-ms)]
|
|
77
|
+
(enqueue! step plan)
|
|
78
|
+
(case (:op step)
|
|
79
|
+
"apply-config"
|
|
80
|
+
(do
|
|
81
|
+
(swap! state-atom state/configure (:config command))
|
|
82
|
+
(emit-status! emit-event state-atom (:status @state-atom) "configured"))
|
|
83
|
+
|
|
84
|
+
"start-session"
|
|
85
|
+
(do
|
|
86
|
+
(swap! state-atom state/mark-started now)
|
|
87
|
+
(emit-status! emit-event state-atom "listening" nil))
|
|
88
|
+
|
|
89
|
+
"stop-session"
|
|
90
|
+
(do
|
|
91
|
+
(swap! state-atom state/mark-stopped now (:reason step))
|
|
92
|
+
(emit-status! emit-event state-atom "idle" (:reason step)))
|
|
93
|
+
|
|
94
|
+
"reset-state"
|
|
95
|
+
(do
|
|
96
|
+
(swap! state-atom state/reset-state)
|
|
97
|
+
(emit-status! emit-event state-atom "idle" "reset"))
|
|
98
|
+
|
|
99
|
+
"record-user-utterance"
|
|
100
|
+
(swap! state-atom state/record-user-utterance (:text step) now (:source step))
|
|
101
|
+
|
|
102
|
+
"emit-user-utterance"
|
|
103
|
+
(emit-event {:type "conversation.userUtterance"
|
|
104
|
+
:agency "conversation"
|
|
105
|
+
:text (:text step)
|
|
106
|
+
:source (:source step)
|
|
107
|
+
:turnId (:turnId @state-atom)
|
|
108
|
+
:at now})
|
|
109
|
+
|
|
110
|
+
"request-response"
|
|
111
|
+
(let [request (:request step)]
|
|
112
|
+
(swap! state-atom state/record-response-request request)
|
|
113
|
+
(emit-event (assoc request
|
|
114
|
+
:type "conversation.requestResponse"
|
|
115
|
+
:agency "conversation"
|
|
116
|
+
:targetAgency "conversation-provider")))
|
|
117
|
+
|
|
118
|
+
"invoke-provider"
|
|
119
|
+
(invoke-provider! (:request step))
|
|
120
|
+
|
|
121
|
+
"cancel-provider"
|
|
122
|
+
(when-let [request-id (:requestId step)]
|
|
123
|
+
((:cancel provider-effector) request-id))
|
|
124
|
+
|
|
125
|
+
"record-agent-utterance"
|
|
126
|
+
(do
|
|
127
|
+
(swap! state-atom state/record-agent-utterance (:text step) now (:source step))
|
|
128
|
+
(emit-event {:type "conversation.agentUtterance"
|
|
129
|
+
:agency "conversation"
|
|
130
|
+
:text (:text step)
|
|
131
|
+
:source (:source step)
|
|
132
|
+
:turnId (:turnId @state-atom)
|
|
133
|
+
:at now}))
|
|
134
|
+
|
|
135
|
+
"request-tts"
|
|
136
|
+
(request-tts! step now)
|
|
137
|
+
|
|
138
|
+
"record-provider-error"
|
|
139
|
+
(swap! state-atom state/record-provider-error
|
|
140
|
+
(:requestId step)
|
|
141
|
+
(:message step)
|
|
142
|
+
now)
|
|
143
|
+
|
|
144
|
+
"publish-provider-error"
|
|
145
|
+
(emit-event {:type "conversation.providerError"
|
|
146
|
+
:agency "conversation"
|
|
147
|
+
:requestId (:requestId step)
|
|
148
|
+
:turnId (:turnId step)
|
|
149
|
+
:message (:message step)
|
|
150
|
+
:at now})
|
|
151
|
+
|
|
152
|
+
"cancel-turn"
|
|
153
|
+
(swap! state-atom state/record-cancel now (:reason step))
|
|
154
|
+
|
|
155
|
+
"publish-cancel"
|
|
156
|
+
(emit-event {:type "conversation.cancelRequested"
|
|
157
|
+
:agency "conversation"
|
|
158
|
+
:targetAgency (get-in @state-atom [:config :ttsAgency])
|
|
159
|
+
:reason (:reason step)
|
|
160
|
+
:turnId (:turnId @state-atom)
|
|
161
|
+
:at now})
|
|
162
|
+
|
|
163
|
+
"ignore-stale-response"
|
|
164
|
+
(emit-event {:type "conversation.ignored"
|
|
165
|
+
:agency "conversation"
|
|
166
|
+
:reason (:reason step)
|
|
167
|
+
:requestId (:requestId step)
|
|
168
|
+
:turnId (:turnId step)
|
|
169
|
+
:at now})
|
|
170
|
+
|
|
171
|
+
"record-tts-status"
|
|
172
|
+
(swap! state-atom assoc
|
|
173
|
+
:status (or (:status step) (:status @state-atom))
|
|
174
|
+
:lastEvent {:type "conversation.ttsStatus"
|
|
175
|
+
:status (:status step)
|
|
176
|
+
:at now})
|
|
177
|
+
|
|
178
|
+
"publish-status"
|
|
179
|
+
(emit-status! emit-event state-atom (:status step) (:reason step))
|
|
180
|
+
|
|
181
|
+
"fail"
|
|
182
|
+
(emit-event {:type "error"
|
|
183
|
+
:agency "conversation"
|
|
184
|
+
:message (:reason step)
|
|
185
|
+
:commandType (:commandType step)})
|
|
186
|
+
|
|
187
|
+
nil)))]
|
|
188
|
+
{:schedule
|
|
189
|
+
(fn [command plan]
|
|
190
|
+
(when-not @disposed?
|
|
191
|
+
(doseq [step (:steps plan)]
|
|
192
|
+
(execute-step! step command plan))))
|
|
193
|
+
|
|
194
|
+
:queue
|
|
195
|
+
(fn []
|
|
196
|
+
@queue)
|
|
197
|
+
|
|
198
|
+
:dispose
|
|
199
|
+
(fn []
|
|
200
|
+
(reset! disposed? true)
|
|
201
|
+
((:dispose provider-effector))
|
|
202
|
+
(reset! queue []))})))
|