briyah 1.2.0 → 1.2.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/data/common/config/markup +1 -1
- package/data/common/config/model_prices.json +3775 -653
- package/data/common/prompts/character/create_character.json +33 -29
- package/data/common/prompts/character/create_character.prompt +13 -2
- package/data/common/prompts/character/create_user_character.json +32 -28
- package/data/common/prompts/character/create_user_character.prompt +13 -2
- package/data/common/prompts/character/progress_character.json +22 -18
- package/data/common/prompts/character/progress_character.prompt +6 -2
- package/data/common/prompts/illustrator/describe_character.prompt +31 -0
- package/data/common/prompts/illustrator/describe_scene.json +19 -1
- package/data/common/prompts/illustrator/describe_scene.prompt +2 -1
- package/data/common/prompts/narrator/perceive.prompt +96 -35
- package/data/common/prompts/story_moderator/moderate.json +33 -7
- package/data/common/prompts/story_moderator/moderate.prompt +166 -20
- package/dist-sdk/server/src/ai/LLM/anthropic.service.d.ts +2 -0
- package/dist-sdk/server/src/ai/LLM/anthropic.service.js +25 -13
- package/dist-sdk/server/src/ai/LLM/base-ai.service.d.ts +3 -0
- package/dist-sdk/server/src/ai/LLM/base-ai.service.js +10 -0
- package/dist-sdk/server/src/ai/LLM/deepseek.service.js +10 -0
- package/dist-sdk/server/src/ai/LLM/googleai.service.js +11 -8
- package/dist-sdk/server/src/ai/LLM/grok.service.js +8 -5
- package/dist-sdk/server/src/ai/LLM/openai.service.js +12 -11
- package/dist-sdk/server/src/ai/LLM/together.service.js +9 -6
- package/dist-sdk/server/src/ai/agent.d.ts +4 -3
- package/dist-sdk/server/src/ai/agent.js +12 -5
- package/dist-sdk/server/src/ai/ai-debug-logger.d.ts +10 -0
- package/dist-sdk/server/src/ai/ai-debug-logger.js +82 -0
- package/dist-sdk/server/src/room/message.d.ts +1 -1
- package/dist-sdk/server/src/room/message.js +1 -1
- package/dist-sdk/server/src/room/room.d.ts +1 -0
- package/dist-sdk/server/src/room/room.js +43 -19
- package/dist-sdk/server/src/sdk/briyah-config.d.ts +1 -0
- package/dist-sdk/server/src/sdk/briyah-config.js +4 -0
- package/dist-sdk/server/src/story/story.service.d.ts +7 -0
- package/dist-sdk/server/src/story/story.service.js +221 -87
- package/dist-sdk/shared/types/app.types.d.ts +1 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/Agent.html +15 -16
- package/docs/classes/Briyah.html +12 -12
- package/docs/classes/BriyahConfigService.html +5 -5
- package/docs/classes/Room.html +24 -24
- package/docs/classes/RoomMessage.html +11 -11
- package/docs/enums/MessageAction.html +3 -3
- package/docs/hierarchy.html +1 -1
- package/docs/index.html +2 -2
- package/docs/interfaces/AgentInfo.html +2 -2
- package/docs/interfaces/AgentMessagesResponse.html +2 -2
- package/docs/interfaces/AppService.html +110 -110
- package/docs/interfaces/Artifact.html +3 -3
- package/docs/interfaces/ArtifactMetadata.html +2 -2
- package/docs/interfaces/AttachDocumentResponse.html +2 -2
- package/docs/interfaces/BriyahConfigOptions.html +7 -7
- package/docs/interfaces/ChapterInfo.html +2 -2
- package/docs/interfaces/Character.html +2 -2
- package/docs/interfaces/CreateAgentResponse.html +2 -2
- package/docs/interfaces/CreateRoomResponse.html +2 -2
- package/docs/interfaces/CreateStoryResponse.html +2 -2
- package/docs/interfaces/FileList.html +2 -2
- package/docs/interfaces/LoggingOptions.html +10 -6
- package/docs/interfaces/Message.html +2 -2
- package/docs/interfaces/ModelInfo.html +2 -2
- package/docs/interfaces/PreparedPromptResponse.html +2 -2
- package/docs/interfaces/ProcessTextResponse.html +2 -2
- package/docs/interfaces/PromptFile.html +2 -2
- package/docs/interfaces/PromptFileContent.html +2 -2
- package/docs/interfaces/PromptFilesResponse.html +2 -2
- package/docs/interfaces/PromptFolder.html +2 -2
- package/docs/interfaces/PromptFoldersResponse.html +2 -2
- package/docs/interfaces/RoomDetails.html +2 -2
- package/docs/interfaces/RoomInfo.html +2 -2
- package/docs/interfaces/RoomMessagesResponse.html +2 -2
- package/docs/interfaces/StoryErrorEvent.html +3 -3
- package/docs/interfaces/StoryIdea.html +2 -2
- package/docs/interfaces/StoryInfo.html +3 -3
- package/docs/interfaces/StoryIntroduceCharacterEvent.html +3 -3
- package/docs/interfaces/StoryProgressChapterEvent.html +3 -3
- package/docs/interfaces/StoryState.html +5 -5
- package/docs/interfaces/StoryStateEvent.html +3 -3
- package/docs/interfaces/Transaction.html +2 -2
- package/docs/interfaces/TransactionHistoryResponse.html +2 -2
- package/docs/modules.html +1 -1
- package/docs/types/PromptScope.html +1 -1
- package/package.json +2 -1
|
@@ -21,6 +21,13 @@ Filter inappropriate language and content from messages.
|
|
|
21
21
|
### Human Character Name
|
|
22
22
|
{{humanAgentName}}
|
|
23
23
|
|
|
24
|
+
### Character Inventories and Conditions
|
|
25
|
+
You have access to each character's `[CharacterName] - Inventory` artifact (visible only to you, the Narrator, and the character themselves). These artifacts list what each character is currently carrying, wearing, or possessing, plus any notable physical or mental conditions (wounded, exhausted, drunk, transformed, etc.). Use these to keep the story self-consistent — when a character refers to "my sword" the inventory should support it, when a character takes damage you should record the wound, etc.
|
|
26
|
+
|
|
27
|
+
{{#if visibleArtifacts}}
|
|
28
|
+
{{visibleArtifacts}}
|
|
29
|
+
{{/if}}
|
|
30
|
+
|
|
24
31
|
|
|
25
32
|
## Your Task
|
|
26
33
|
Based upon the provided context, determine:
|
|
@@ -32,6 +39,13 @@ To accomplish your task:
|
|
|
32
39
|
- Analyze the Current Situation in the context of the Story Scenario and your (limited) message history
|
|
33
40
|
- Determine whether the Sender of the recent message is trying to 1) perform an action or 2) only participate in dialog
|
|
34
41
|
|
|
42
|
+
### The Current Situation is authoritative
|
|
43
|
+
The **Current Situation** line is the single source of truth about which characters are physically present in the active scene, and where. When routing:
|
|
44
|
+
- Only characters named in the Current Situation are "present". Other characters are elsewhere and cannot hear or respond unless the Current Situation itself changes.
|
|
45
|
+
- Do **not** infer that a character is present from vague references in the body of a narration (e.g., "the men outside", "the others", "everyone"). If a character is not named in the Current Situation, treat them as absent — pick a different responder.
|
|
46
|
+
- Do **not** assume a scene shift based on body content alone. A scene shift only happens when the Current Situation itself names a different time, place, or set of characters. If the situation still says "Jessica and Mòr in the kitchen", the responder must be one of Jessica, Mòr, or the Narrator — never a character who is miles away in another storyline.
|
|
47
|
+
- If the narration genuinely describes a character who is not in the Current Situation (e.g., a brief aside about someone elsewhere), keep the responder among the characters who ARE in the Current Situation; do not hand the turn off to the absent character.
|
|
48
|
+
|
|
35
49
|
### If the Sender is intending to perform an action:
|
|
36
50
|
Route the message to the Narrator.
|
|
37
51
|
The Narrator will interpret the message and narrate the outcome of the action.
|
|
@@ -53,11 +67,26 @@ This request will *not* become part of the readable story, only the narrator's r
|
|
|
53
67
|
3. Responder: The one character you choose to respond next to best maintain the flow of the conversation and further the story.
|
|
54
68
|
4. Targets: List of all characters who would realistically be able to hear the message (even if they shouldn't respond).
|
|
55
69
|
- When processing a message from the Narrator, the targets array should only include characters who are present for the situation being narrated.
|
|
56
|
-
5.
|
|
57
|
-
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
-
|
|
70
|
+
5. Changes: An object containing any story state changes triggered by this message.
|
|
71
|
+
- `update_portrait`: list the names of any characters whose visible appearance has just changed in a way that should be reflected in their portrait image.
|
|
72
|
+
- Include a character's name when the current message describes a meaningful change to their visible appearance: new or different clothing/outfit, significant injury (wounds, blood, bandages), physical transformation, or a notable item they are now visibly carrying or wearing.
|
|
73
|
+
- Use the character's exact name as it appears in the Available Characters list.
|
|
74
|
+
- Leave the array empty if no visible appearance changes occurred.
|
|
75
|
+
- Minor or invisible changes (emotional state alone, unseen actions) do NOT warrant a portrait update.
|
|
76
|
+
- `update_inventory`: list of character inventory artifacts that should be rewritten in light of this message. Each entry replaces the named character's `[Name] - Inventory` artifact in full.
|
|
77
|
+
- Include an entry when items are gained, lost, consumed, broken, given, taken, equipped, unequipped, OR when a notable physical/mental trait or condition changes (wounded, healed, exhausted, intoxicated, transformed, cursed, blessed, etc.).
|
|
78
|
+
- The `inventory` field must contain the COMPLETE new inventory body (not a diff). Read the character's current inventory artifact from the context above, apply the change, and return the updated artifact text.
|
|
79
|
+
- Use this structure (omit a section if it would be empty):
|
|
80
|
+
```
|
|
81
|
+
## Items
|
|
82
|
+
- <item 1>
|
|
83
|
+
- <item 2>
|
|
84
|
+
|
|
85
|
+
## Conditions
|
|
86
|
+
- <condition 1>
|
|
87
|
+
```
|
|
88
|
+
- Keep it concise — names of items, not flavor prose. Conditions should be short phrases ("wounded right arm", "exhausted").
|
|
89
|
+
- Leave the array empty if no inventory or condition changes occurred.
|
|
61
90
|
|
|
62
91
|
## Content Moderation
|
|
63
92
|
- Do *not* allow profanity or swear words like 'hell', 'shit', and 'damn' or 'damned'.
|
|
@@ -73,7 +102,10 @@ You must respond with a valid JSON object in the following format:
|
|
|
73
102
|
"content": "relay", // or modified message body
|
|
74
103
|
"name": "CharacterName1", // Name of character who should respond next
|
|
75
104
|
"targets": ["CharacterName1", "CharacterName2"], // List of all characters who would realistically be able to hear the message
|
|
76
|
-
"
|
|
105
|
+
"changes": {
|
|
106
|
+
"update_portrait": [], // Names of characters whose visible appearance changed; empty if none
|
|
107
|
+
"update_inventory": [] // Inventory/condition updates; empty if none
|
|
108
|
+
}
|
|
77
109
|
}
|
|
78
110
|
|
|
79
111
|
## Conversation Rules
|
|
@@ -86,6 +118,18 @@ You must respond with a valid JSON object in the following format:
|
|
|
86
118
|
- When a character sends "PASS", route to Narrator with PASS command (relay_silent), or to another contextually appropriate character if the situation calls for it.
|
|
87
119
|
- **Important**:Avoid lengthy conversations that exclude the Human Character completely. Give the Human Character a chance to interact with the story.
|
|
88
120
|
|
|
121
|
+
### When no character is available to respond — auto-PASS to the Narrator
|
|
122
|
+
Sometimes no character in the Current Situation can realistically be the next speaker. The most common case is when the only present character is asleep, unconscious, dead, paralyzed, in a coma, or otherwise narratively unable to speak or act (check their `[Name] - Inventory` artifact's `## Conditions` section). It can also happen when the Current Situation is a transitional beat with no living character actively present.
|
|
123
|
+
|
|
124
|
+
In these cases, emit an automatic PASS on the human's behalf, exactly as you would if the Human Character had typed "PASS":
|
|
125
|
+
- Set `name` to `"Narrator"` — the Narrator can always continue the story.
|
|
126
|
+
- Keep `action` as `"relay"` so the original narration is still logged to the readable story.
|
|
127
|
+
- Keep `targets` as the characters who are present in the scene (even if all of them are asleep — they can still "hear" the narration in a passive sense).
|
|
128
|
+
|
|
129
|
+
The Narrator can always respond and is the safe fallback. Do this whether the unavailable character is the Human Character or an AI character — the rule is about who can speak in the *story*, not about who is at the keyboard.
|
|
130
|
+
|
|
131
|
+
Note: "Resting", "tired", "lightly intoxicated", "wounded but conscious" are NOT disqualifying — those characters can still respond. The bar is whether they could realistically speak or act in the next beat of the story.
|
|
132
|
+
|
|
89
133
|
## Introductions
|
|
90
134
|
- Only set "name" to the exact character name (nickname) of one of the characters in the **Available Characters** list.
|
|
91
135
|
- If a message from the Human Character (or any character) directly addresses someone who is NOT listed in the **Available Characters** list, route the message to the Narrator. The Narrator has access to the secret plot plan and can identify whether the addressed person is a planned character; do NOT guess at a name yourself.
|
|
@@ -112,7 +156,7 @@ Correct Response:
|
|
|
112
156
|
"content": "PASS",
|
|
113
157
|
"name": "Narrator",
|
|
114
158
|
"targets": ["Narrator"],
|
|
115
|
-
"update_portrait": []
|
|
159
|
+
"changes": { "update_portrait": [] }
|
|
116
160
|
}
|
|
117
161
|
- Mike is choosing not to act or speak, so select another appropriate character (or the narrator) to speak next instead.
|
|
118
162
|
- Use "relay_silent" because PASS is a meta-action and shouldn't appear in the story
|
|
@@ -126,7 +170,7 @@ Correct Response:
|
|
|
126
170
|
"content": "relay",
|
|
127
171
|
"name": "Mike",
|
|
128
172
|
"targets": ["Mike", "Bob"],
|
|
129
|
-
"update_portrait": []
|
|
173
|
+
"changes": { "update_portrait": [] }
|
|
130
174
|
}
|
|
131
175
|
- According to the current situation, only Mike, Jessica, and Bob are within hearing distance of each other.
|
|
132
176
|
- Jessica is speaking, and only Mike and Bob can hear. Sergei and Carter are not present.
|
|
@@ -143,7 +187,7 @@ Correct Response:
|
|
|
143
187
|
"content": "relay",
|
|
144
188
|
"name": "Jessica",
|
|
145
189
|
"targets": ["Jessica", "Bob"],
|
|
146
|
-
"update_portrait": []
|
|
190
|
+
"changes": { "update_portrait": [] }
|
|
147
191
|
}
|
|
148
192
|
- According to the current situation, only Mike, Jessica, and Bob are within hearing distance of each other.
|
|
149
193
|
- Mike is speaking, and only Jessica and Bob can hear. Sergei and Carter are not present.
|
|
@@ -160,7 +204,7 @@ Correct Response:
|
|
|
160
204
|
"content": "relay",
|
|
161
205
|
"name": "Narrator",
|
|
162
206
|
"targets": ["Narrator"],
|
|
163
|
-
"update_portrait": []
|
|
207
|
+
"changes": { "update_portrait": [] }
|
|
164
208
|
}
|
|
165
209
|
- The sender is attempting to perform an action with the message
|
|
166
210
|
- Only the Narrator is able to handle the character's request to perform an action.
|
|
@@ -178,7 +222,7 @@ Correct Response:
|
|
|
178
222
|
"content": "relay",
|
|
179
223
|
"name": "Mike",
|
|
180
224
|
"targets": ["Mike", "Jessica", "Bob"],
|
|
181
|
-
"update_portrait": []
|
|
225
|
+
"changes": { "update_portrait": [] }
|
|
182
226
|
}
|
|
183
227
|
- This is not dialog and there is no obvious responder, so choose the human character to respond because they are involved in the current situation.
|
|
184
228
|
- All characters involved in the current situation should be targets of the message
|
|
@@ -194,13 +238,32 @@ Correct Response:
|
|
|
194
238
|
"content": "relay",
|
|
195
239
|
"name": "Sergei",
|
|
196
240
|
"targets": ["Sergei", "Carter"],
|
|
197
|
-
"update_portrait": []
|
|
241
|
+
"changes": { "update_portrait": [] }
|
|
198
242
|
}
|
|
199
243
|
- This is not dialog and there is no obvious responder, so choose an character involved in the current situation to be the responder
|
|
200
244
|
- All characters involved in the current situation should be targets of the message
|
|
201
245
|
- Mike, Jessica, and Bob should not be targets of this message because they are not involved in the situation.
|
|
202
246
|
|
|
203
247
|
|
|
248
|
+
### Example 5b: Narration mentions an absent character, but the Current Situation has NOT shifted
|
|
249
|
+
Current Situation: 17:35 - Mike and Jessica are in the kitchen as the evening meal is being prepared
|
|
250
|
+
Recent Message Sender: Narrator
|
|
251
|
+
Recent Message: The kitchen smells of stew and woodsmoke. Outside, the men are tending to the horses. Somewhere else, Sergei is at his cottage that morning, washing at the basin and turning over what he saw the night before.
|
|
252
|
+
Correct Response:
|
|
253
|
+
{
|
|
254
|
+
"action": "relay",
|
|
255
|
+
"content": "relay",
|
|
256
|
+
"name": "Mike",
|
|
257
|
+
"targets": ["Mike", "Jessica"],
|
|
258
|
+
"changes": { "update_portrait": [] }
|
|
259
|
+
}
|
|
260
|
+
- The Current Situation places Mike and Jessica in the kitchen — that is authoritative. Sergei is NOT in the Current Situation, even though the narrator mentions him in passing.
|
|
261
|
+
- Do not let the brief reference to Sergei convince you the scene has shifted. A scene shift requires the Current Situation itself to name new characters or a new place.
|
|
262
|
+
- Do not let the phrase "the men" make you pick a named character — "the men" is a vague reference, not a routing instruction.
|
|
263
|
+
- The responder must be a character present in the Current Situation. Mike (the Human Character) is present, so route to Mike.
|
|
264
|
+
- Targets are only the characters in the Current Situation; do not include Sergei or any absent characters.
|
|
265
|
+
|
|
266
|
+
|
|
204
267
|
### Example 6: Character addresses someone not listed in the Available Characters list
|
|
205
268
|
Recent Message Sender: Mike (human character)
|
|
206
269
|
Recent Message: Hello, Albert Flanagan!
|
|
@@ -210,7 +273,7 @@ Correct Response:
|
|
|
210
273
|
"content": "relay",
|
|
211
274
|
"name": "Narrator",
|
|
212
275
|
"targets": ["Narrator"],
|
|
213
|
-
"update_portrait": []
|
|
276
|
+
"changes": { "update_portrait": [] }
|
|
214
277
|
}
|
|
215
278
|
- "Albert Flanagan" is not in the Available Characters list.
|
|
216
279
|
- Route to the Narrator. The Narrator can consult the plot plan to identify the addressed character (and emit an appropriate INTRODUCE situation) or narrate an alternative response.
|
|
@@ -225,7 +288,7 @@ Correct Response:
|
|
|
225
288
|
"content": "[message content modified to remove explicit content]",
|
|
226
289
|
"name": "Narrator",
|
|
227
290
|
"targets": ["Narrator"],
|
|
228
|
-
"update_portrait": []
|
|
291
|
+
"changes": { "update_portrait": [] }
|
|
229
292
|
}
|
|
230
293
|
- If the message contains explicit content, just remove the explicit content if possible or modify the message to something more appropriate.
|
|
231
294
|
|
|
@@ -238,37 +301,120 @@ Correct Response:
|
|
|
238
301
|
"content": "relay",
|
|
239
302
|
"name": "",
|
|
240
303
|
"targets": [],
|
|
241
|
-
"update_portrait": []
|
|
304
|
+
"changes": { "update_portrait": [] }
|
|
242
305
|
}
|
|
243
306
|
- Use the relay_silent action and leave the name field blank and set the targets list to an empty array.
|
|
244
307
|
- The message will be silently relayed to the Narrator to perform the introduction.
|
|
245
308
|
|
|
246
309
|
|
|
247
|
-
### Example 9: Narrator describes a visible appearance change
|
|
310
|
+
### Example 9: Narrator describes a visible appearance change (and a wound)
|
|
248
311
|
Recent Message Sender: Narrator
|
|
249
312
|
Recent Message: The arrow grazes Jessica's arm, tearing her sleeve and leaving a bloody gash. She wraps a strip of cloth around the wound and keeps moving.
|
|
313
|
+
Suppose Jessica's existing inventory is:
|
|
314
|
+
## Items
|
|
315
|
+
- Hunting bow
|
|
316
|
+
- Quiver with 7 arrows
|
|
317
|
+
- Hunting knife
|
|
250
318
|
Correct Response:
|
|
251
319
|
{
|
|
252
320
|
"action": "relay",
|
|
253
321
|
"content": "relay",
|
|
254
322
|
"name": "Mike",
|
|
255
323
|
"targets": ["Mike", "Jessica", "Bob"],
|
|
256
|
-
"
|
|
324
|
+
"changes": {
|
|
325
|
+
"update_portrait": ["Jessica"],
|
|
326
|
+
"update_inventory": [
|
|
327
|
+
{
|
|
328
|
+
"characterName": "Jessica",
|
|
329
|
+
"inventory": "## Items\n- Hunting bow\n- Quiver with 7 arrows\n- Hunting knife\n\n## Conditions\n- Wounded right arm (bandaged)"
|
|
330
|
+
}
|
|
331
|
+
]
|
|
332
|
+
}
|
|
257
333
|
}
|
|
258
334
|
- Jessica's visible appearance has changed (torn sleeve, wound, makeshift bandage), so her portrait should be updated.
|
|
335
|
+
- A wound is a condition worth tracking in her inventory artifact. Carry forward her existing items unchanged, and add the new condition.
|
|
259
336
|
- All characters present in the current situation are targets.
|
|
260
337
|
|
|
261
338
|
|
|
262
|
-
### Example 10: Character action with no visible change
|
|
339
|
+
### Example 10: Character action with no visible change (item gained)
|
|
263
340
|
Recent Message Sender: Bob
|
|
264
341
|
Recent Message: Bob wields his new sword proudly above his head
|
|
342
|
+
Suppose Bob's existing inventory is:
|
|
343
|
+
## Items
|
|
344
|
+
- Bedroll
|
|
345
|
+
- Tinderbox
|
|
265
346
|
Correct Response:
|
|
266
347
|
{
|
|
267
348
|
"action": "relay_silent",
|
|
268
349
|
"content": "relay",
|
|
269
350
|
"name": "Narrator",
|
|
270
351
|
"targets": ["Narrator"],
|
|
271
|
-
"
|
|
352
|
+
"changes": {
|
|
353
|
+
"update_portrait": ["Bob"],
|
|
354
|
+
"update_inventory": [
|
|
355
|
+
{
|
|
356
|
+
"characterName": "Bob",
|
|
357
|
+
"inventory": "## Items\n- Bedroll\n- Tinderbox\n- Iron sword"
|
|
358
|
+
}
|
|
359
|
+
]
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
- Bob just got a new weapon, so it should appear in his portrait AND be added to his inventory items.
|
|
363
|
+
- The inventory field carries the FULL updated artifact body, not just the new item.
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
### Example 11: Pure inventory change with no visual update
|
|
367
|
+
Recent Message Sender: Narrator
|
|
368
|
+
Recent Message: Mike takes a long pull from the whiskey flask. The warmth spreads through his chest as the world softens at the edges.
|
|
369
|
+
Suppose Mike's existing inventory is:
|
|
370
|
+
## Items
|
|
371
|
+
- Whiskey flask (3/4 full)
|
|
372
|
+
- Wool blanket
|
|
373
|
+
Correct Response:
|
|
374
|
+
{
|
|
375
|
+
"action": "relay",
|
|
376
|
+
"content": "relay",
|
|
377
|
+
"name": "Mike",
|
|
378
|
+
"targets": ["Mike", "Jessica", "Bob"],
|
|
379
|
+
"changes": {
|
|
380
|
+
"update_portrait": [],
|
|
381
|
+
"update_inventory": [
|
|
382
|
+
{
|
|
383
|
+
"characterName": "Mike",
|
|
384
|
+
"inventory": "## Items\n- Whiskey flask (1/2 full)\n- Wool blanket\n\n## Conditions\n- Lightly intoxicated"
|
|
385
|
+
}
|
|
386
|
+
]
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
- Mike consumed some whiskey, so the flask amount decreased and he is now mildly drunk.
|
|
390
|
+
- No visible appearance change, so update_portrait is empty.
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
### Example 12: No character available to respond — auto-PASS to Narrator
|
|
394
|
+
Current Situation: 23:50 - Jessica is alone in the small side room at Balmore, having just settled in for the night
|
|
395
|
+
Suppose Jessica's inventory is:
|
|
396
|
+
## Items
|
|
397
|
+
- Wool blanket
|
|
398
|
+
- Water avens root (wrapped in cloth)
|
|
399
|
+
|
|
400
|
+
## Conditions
|
|
401
|
+
- Asleep
|
|
402
|
+
Recent Message Sender: Narrator
|
|
403
|
+
Recent Message: The wind softens to a hush against the shutter. Jessica's breathing slows and evens. The candle gutters once, then steadies, throwing long shadows on the wattle wall.
|
|
404
|
+
Correct Response:
|
|
405
|
+
{
|
|
406
|
+
"action": "relay",
|
|
407
|
+
"content": "relay",
|
|
408
|
+
"name": "Narrator",
|
|
409
|
+
"targets": ["Jessica"],
|
|
410
|
+
"changes": {
|
|
411
|
+
"update_portrait": [],
|
|
412
|
+
"update_inventory": []
|
|
413
|
+
}
|
|
272
414
|
}
|
|
273
|
-
-
|
|
415
|
+
- Jessica is the only character in the scene, but her conditions say **Asleep** — she cannot speak or act in the story right now. There is no one to respond.
|
|
416
|
+
- Auto-PASS to the **Narrator** by setting `name` to "Narrator". The Narrator can always continue the story — transition to the next scene, advance time, cut to another character, etc.
|
|
417
|
+
- Keep `action: "relay"` so the narrator's atmospheric narration is still logged to the readable story.
|
|
418
|
+
- Jessica stays in `targets` because she's physically present in the scene (and could be woken later), but she is NOT the responder `name`.
|
|
419
|
+
- "Asleep" is already in her conditions; no inventory update needed.
|
|
274
420
|
|
|
@@ -24,5 +24,7 @@ export declare class AnthropicAiService extends BaseAiService {
|
|
|
24
24
|
fetchModelsFromApi(): Promise<ModelInfo[]>;
|
|
25
25
|
addToConversationHistory(agent: Agent, message: string, fromSelf?: boolean): void;
|
|
26
26
|
textPrompt(agent: Agent, prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, cacheConfig?: any, maxOutputChars?: number): Promise<any>;
|
|
27
|
+
private static readonly EFFORT_CONFIG_MODEL_VERSIONS;
|
|
28
|
+
private usesEffortConfig;
|
|
27
29
|
private computeMessageCost;
|
|
28
30
|
}
|
|
@@ -8,6 +8,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
8
8
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
|
+
var AnthropicAiService_1;
|
|
11
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
13
|
exports.AnthropicAiService = void 0;
|
|
13
14
|
const common_1 = require("@nestjs/common");
|
|
@@ -17,6 +18,7 @@ const model_prices_1 = require("../model_prices");
|
|
|
17
18
|
const errors_1 = require("../../common/errors");
|
|
18
19
|
const logger_1 = require("../../common/logger");
|
|
19
20
|
let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.BaseAiService {
|
|
21
|
+
static { AnthropicAiService_1 = this; }
|
|
20
22
|
anthropic;
|
|
21
23
|
constructor() {
|
|
22
24
|
super();
|
|
@@ -211,32 +213,38 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
|
|
|
211
213
|
max_tokens: maxOutputTokens,
|
|
212
214
|
tools: tools.length > 0 ? tools : undefined,
|
|
213
215
|
};
|
|
214
|
-
if (agent.modelName
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
216
|
+
if (this.usesEffortConfig(agent.modelName)) {
|
|
217
|
+
if (agent.reasoningEffort) {
|
|
218
|
+
createParams.thinking = { type: 'adaptive' };
|
|
219
|
+
createParams.output_config = { effort: agent.reasoningEffort };
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (thinkingTokens > 0) {
|
|
223
|
+
createParams.thinking = { type: 'enabled', budget_tokens: thinkingTokens };
|
|
218
224
|
}
|
|
219
225
|
else {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
else {
|
|
224
|
-
createParams.thinking = { type: 'disabled' };
|
|
225
|
-
createParams.temperature = 0;
|
|
226
|
-
}
|
|
226
|
+
createParams.thinking = { type: 'disabled' };
|
|
227
|
+
createParams.temperature = 0;
|
|
227
228
|
}
|
|
229
|
+
this.debugLogRequest(agent.agentName, createParams);
|
|
228
230
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
229
231
|
try {
|
|
230
232
|
const response = await this.anthropic.messages.create(createParams);
|
|
233
|
+
let thinkingText = '';
|
|
231
234
|
let responseText = '';
|
|
232
235
|
if (response.content && response.content.length > 0) {
|
|
233
236
|
for (let i = 0; i < response.content.length; i++) {
|
|
234
237
|
const content = response.content[i];
|
|
235
|
-
if ('
|
|
238
|
+
if (content.type === 'thinking' && 'thinking' in content) {
|
|
239
|
+
thinkingText += content.thinking;
|
|
240
|
+
}
|
|
241
|
+
else if ('text' in content) {
|
|
236
242
|
responseText += content.text;
|
|
237
243
|
}
|
|
238
244
|
}
|
|
239
245
|
}
|
|
246
|
+
this.debugLogThinking(agent.agentName, thinkingText);
|
|
247
|
+
this.debugLogResponse(agent.agentName, responseText);
|
|
240
248
|
if (jsonSchema) {
|
|
241
249
|
responseText = this.trimToJson(responseText);
|
|
242
250
|
responseText = this.sanitizeJsonString(responseText);
|
|
@@ -308,6 +316,10 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
|
|
|
308
316
|
}
|
|
309
317
|
throw new Error(`Failed to get valid response after ${maxRetries} attempts. Last error: ${lastError?.message || 'Unknown error'}`);
|
|
310
318
|
}
|
|
319
|
+
static EFFORT_CONFIG_MODEL_VERSIONS = ['4-7', '4-8'];
|
|
320
|
+
usesEffortConfig(modelName) {
|
|
321
|
+
return AnthropicAiService_1.EFFORT_CONFIG_MODEL_VERSIONS.some((v) => modelName.includes(v));
|
|
322
|
+
}
|
|
311
323
|
computeMessageCost(agent, usage) {
|
|
312
324
|
const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
313
325
|
if (!modelInfo) {
|
|
@@ -340,7 +352,7 @@ let AnthropicAiService = class AnthropicAiService extends base_ai_service_1.Base
|
|
|
340
352
|
}
|
|
341
353
|
};
|
|
342
354
|
exports.AnthropicAiService = AnthropicAiService;
|
|
343
|
-
exports.AnthropicAiService = AnthropicAiService = __decorate([
|
|
355
|
+
exports.AnthropicAiService = AnthropicAiService = AnthropicAiService_1 = __decorate([
|
|
344
356
|
(0, common_1.Injectable)(),
|
|
345
357
|
__metadata("design:paramtypes", [])
|
|
346
358
|
], AnthropicAiService);
|
|
@@ -39,6 +39,9 @@ export declare class BaseAiService {
|
|
|
39
39
|
finishAgent(_agent: Agent): Promise<void>;
|
|
40
40
|
addToConversationHistory(agent: Agent, message: string, fromSelf?: boolean, developer?: boolean): void;
|
|
41
41
|
formatMessage(message: any): string;
|
|
42
|
+
protected debugLogRequest(agentName: string, content: unknown): void;
|
|
43
|
+
protected debugLogThinking(agentName: string, thinking: string): void;
|
|
44
|
+
protected debugLogResponse(agentName: string, response: string): void;
|
|
42
45
|
trimResponseText(str: string): string;
|
|
43
46
|
trimToJson(str: string): string;
|
|
44
47
|
sanitizeJsonString(str: string): string;
|
|
@@ -52,6 +52,7 @@ const handlebars_1 = require("handlebars");
|
|
|
52
52
|
const errors_1 = require("../../common/errors");
|
|
53
53
|
const logger_1 = require("../../common/logger");
|
|
54
54
|
const model_prices_1 = require("../model_prices");
|
|
55
|
+
const ai_debug_logger_1 = require("../ai-debug-logger");
|
|
55
56
|
let BaseAiService = class BaseAiService {
|
|
56
57
|
modelsCache = null;
|
|
57
58
|
_isAvailable = true;
|
|
@@ -314,6 +315,15 @@ let BaseAiService = class BaseAiService {
|
|
|
314
315
|
}
|
|
315
316
|
return JSON.stringify(message);
|
|
316
317
|
}
|
|
318
|
+
debugLogRequest(agentName, content) {
|
|
319
|
+
ai_debug_logger_1.AiDebugLogger.writeRequest(agentName, content);
|
|
320
|
+
}
|
|
321
|
+
debugLogThinking(agentName, thinking) {
|
|
322
|
+
ai_debug_logger_1.AiDebugLogger.writeThinking(agentName, thinking);
|
|
323
|
+
}
|
|
324
|
+
debugLogResponse(agentName, response) {
|
|
325
|
+
ai_debug_logger_1.AiDebugLogger.writeResponse(agentName, response);
|
|
326
|
+
}
|
|
317
327
|
trimResponseText(str) {
|
|
318
328
|
if (!str) {
|
|
319
329
|
return str;
|
|
@@ -187,6 +187,13 @@ let DeepSeekAiService = class DeepSeekAiService extends base_ai_service_1.BaseAi
|
|
|
187
187
|
});
|
|
188
188
|
}
|
|
189
189
|
let reasoningEffort = agent.reasoningEffort ? agent.reasoningEffort : undefined;
|
|
190
|
+
const requestParams = {
|
|
191
|
+
model: agent.modelName,
|
|
192
|
+
messages: messages,
|
|
193
|
+
temperature: 0,
|
|
194
|
+
reasoningEffort: reasoningEffort,
|
|
195
|
+
};
|
|
196
|
+
this.debugLogRequest(agent.agentName, requestParams);
|
|
190
197
|
try {
|
|
191
198
|
if (jsonSchema) {
|
|
192
199
|
const result = await (0, ai_1.generateObject)({
|
|
@@ -203,6 +210,7 @@ let DeepSeekAiService = class DeepSeekAiService extends base_ai_service_1.BaseAi
|
|
|
203
210
|
if (result.usage) {
|
|
204
211
|
this.computeMessageCost(agent, result.usage);
|
|
205
212
|
}
|
|
213
|
+
this.debugLogResponse(agent.agentName, JSON.stringify(result.object, null, 2));
|
|
206
214
|
if (saveResponse) {
|
|
207
215
|
this.addToConversationHistory(agent, prompt, false);
|
|
208
216
|
this.addToConversationHistory(agent, JSON.stringify(result.object), true);
|
|
@@ -223,6 +231,8 @@ let DeepSeekAiService = class DeepSeekAiService extends base_ai_service_1.BaseAi
|
|
|
223
231
|
if (result.usage) {
|
|
224
232
|
this.computeMessageCost(agent, result.usage);
|
|
225
233
|
}
|
|
234
|
+
this.debugLogThinking(agent.agentName, result.reasoning ?? '');
|
|
235
|
+
this.debugLogResponse(agent.agentName, result.text);
|
|
226
236
|
if (saveResponse) {
|
|
227
237
|
this.addToConversationHistory(agent, prompt, false);
|
|
228
238
|
this.addToConversationHistory(agent, result.text, true);
|
|
@@ -110,16 +110,18 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
|
|
|
110
110
|
generationConfig.responseSchema = schemaOnly;
|
|
111
111
|
}
|
|
112
112
|
const instructions = promptInstructions || agent.systemInstruction;
|
|
113
|
+
const requestParams = {
|
|
114
|
+
model: agent.modelName,
|
|
115
|
+
contents: messages,
|
|
116
|
+
config: {
|
|
117
|
+
systemInstruction: instructions,
|
|
118
|
+
...generationConfig,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
this.debugLogRequest(agent.agentName, requestParams);
|
|
113
122
|
let responseText;
|
|
114
123
|
try {
|
|
115
|
-
const response = await this.googleAI.models.generateContent(
|
|
116
|
-
model: agent.modelName,
|
|
117
|
-
contents: messages,
|
|
118
|
-
config: {
|
|
119
|
-
systemInstruction: instructions,
|
|
120
|
-
...generationConfig,
|
|
121
|
-
},
|
|
122
|
-
});
|
|
124
|
+
const response = await this.googleAI.models.generateContent(requestParams);
|
|
123
125
|
if (response.text) {
|
|
124
126
|
responseText = response.text;
|
|
125
127
|
}
|
|
@@ -136,6 +138,7 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
|
|
|
136
138
|
else {
|
|
137
139
|
responseText = this.trimResponseText(responseText);
|
|
138
140
|
}
|
|
141
|
+
this.debugLogResponse(agent.agentName, responseText);
|
|
139
142
|
if (response.usageMetadata) {
|
|
140
143
|
this.computeMessageCost(agent, response.usageMetadata);
|
|
141
144
|
}
|
|
@@ -179,13 +179,15 @@ let GrokAiService = class GrokAiService extends base_ai_service_1.BaseAiService
|
|
|
179
179
|
content: [{ type: 'text', text: prompt }],
|
|
180
180
|
});
|
|
181
181
|
}
|
|
182
|
+
const requestParams = {
|
|
183
|
+
model: agent.modelName,
|
|
184
|
+
messages: messages,
|
|
185
|
+
store: false,
|
|
186
|
+
};
|
|
187
|
+
this.debugLogRequest(agent.agentName, requestParams);
|
|
182
188
|
let responseText;
|
|
183
189
|
try {
|
|
184
|
-
let response = await this.openai.chat.completions.create(
|
|
185
|
-
model: agent.modelName,
|
|
186
|
-
messages: messages,
|
|
187
|
-
store: false,
|
|
188
|
-
});
|
|
190
|
+
let response = await this.openai.chat.completions.create(requestParams);
|
|
189
191
|
for (const choice of response.choices) {
|
|
190
192
|
if (choice.message.content)
|
|
191
193
|
responseText = choice.message.content;
|
|
@@ -194,6 +196,7 @@ let GrokAiService = class GrokAiService extends base_ai_service_1.BaseAiService
|
|
|
194
196
|
responseText = this.trimToJson(responseText);
|
|
195
197
|
responseText = this.sanitizeJsonString(responseText);
|
|
196
198
|
}
|
|
199
|
+
this.debugLogResponse(agent.agentName, responseText);
|
|
197
200
|
if (response.usage) {
|
|
198
201
|
this.computeMessageCost(agent, response.usage);
|
|
199
202
|
}
|
|
@@ -161,20 +161,20 @@ let OpenAiService = class OpenAiService extends base_ai_service_1.BaseAiService
|
|
|
161
161
|
type: 'text',
|
|
162
162
|
};
|
|
163
163
|
let instructions = promptInstructions || agent.systemInstruction;
|
|
164
|
+
const requestParams = {
|
|
165
|
+
model: agent.modelName,
|
|
166
|
+
reasoning: { effort: agent.reasoningEffort },
|
|
167
|
+
input: messages,
|
|
168
|
+
instructions: instructions,
|
|
169
|
+
store: false,
|
|
170
|
+
text: { format: format },
|
|
171
|
+
tools: tools,
|
|
172
|
+
};
|
|
173
|
+
this.debugLogRequest(agent.agentName, requestParams);
|
|
164
174
|
let response = null;
|
|
165
175
|
let responseText;
|
|
166
176
|
try {
|
|
167
|
-
response = await this.openai.responses.create(
|
|
168
|
-
model: agent.modelName,
|
|
169
|
-
reasoning: { effort: agent.reasoningEffort },
|
|
170
|
-
input: messages,
|
|
171
|
-
instructions: instructions,
|
|
172
|
-
store: false,
|
|
173
|
-
text: {
|
|
174
|
-
format: format,
|
|
175
|
-
},
|
|
176
|
-
tools: tools,
|
|
177
|
-
});
|
|
177
|
+
response = await this.openai.responses.create(requestParams);
|
|
178
178
|
if (jsonSchema) {
|
|
179
179
|
responseText = this.trimToJson(response.output_text);
|
|
180
180
|
responseText = this.sanitizeJsonString(responseText);
|
|
@@ -182,6 +182,7 @@ let OpenAiService = class OpenAiService extends base_ai_service_1.BaseAiService
|
|
|
182
182
|
else {
|
|
183
183
|
responseText = response.output_text;
|
|
184
184
|
}
|
|
185
|
+
this.debugLogResponse(agent.agentName, responseText);
|
|
185
186
|
if (response.usage) {
|
|
186
187
|
this.computeMessageCost(agent, response.usage);
|
|
187
188
|
}
|
|
@@ -120,14 +120,16 @@ let TogetherAiService = class TogetherAiService extends base_ai_service_1.BaseAi
|
|
|
120
120
|
},
|
|
121
121
|
}
|
|
122
122
|
: undefined;
|
|
123
|
+
const requestParams = {
|
|
124
|
+
model: agent.modelName,
|
|
125
|
+
messages: messages,
|
|
126
|
+
temperature: 0,
|
|
127
|
+
response_format: responseFormat,
|
|
128
|
+
};
|
|
129
|
+
this.debugLogRequest(agent.agentName, requestParams);
|
|
123
130
|
let responseText;
|
|
124
131
|
try {
|
|
125
|
-
const response = await this.togetherClient.chat.completions.create(
|
|
126
|
-
model: agent.modelName,
|
|
127
|
-
messages: messages,
|
|
128
|
-
temperature: 0,
|
|
129
|
-
response_format: responseFormat,
|
|
130
|
-
});
|
|
132
|
+
const response = await this.togetherClient.chat.completions.create(requestParams);
|
|
131
133
|
if (response.choices && response.choices[0]?.message?.content) {
|
|
132
134
|
responseText = response.choices[0].message.content;
|
|
133
135
|
}
|
|
@@ -141,6 +143,7 @@ let TogetherAiService = class TogetherAiService extends base_ai_service_1.BaseAi
|
|
|
141
143
|
else {
|
|
142
144
|
responseText = this.trimResponseText(responseText);
|
|
143
145
|
}
|
|
146
|
+
this.debugLogResponse(agent.agentName, responseText);
|
|
144
147
|
if (response.usage) {
|
|
145
148
|
this.computeMessageCost(agent, response.usage);
|
|
146
149
|
}
|
|
@@ -42,7 +42,6 @@ export declare class Agent {
|
|
|
42
42
|
modelName: string;
|
|
43
43
|
smallModelName?: string;
|
|
44
44
|
serviceName: string;
|
|
45
|
-
imageProperties?: Record<string, any>;
|
|
46
45
|
reasoningEffort: 'low' | 'medium' | 'high' | null;
|
|
47
46
|
allowSearch: boolean;
|
|
48
47
|
maxOutputTokens: number;
|
|
@@ -76,15 +75,17 @@ export declare class Agent {
|
|
|
76
75
|
success: boolean;
|
|
77
76
|
originalLength: number;
|
|
78
77
|
newLength: number;
|
|
78
|
+
originalChars: number;
|
|
79
|
+
newChars: number;
|
|
79
80
|
message: string;
|
|
80
81
|
summary?: string;
|
|
81
82
|
}>;
|
|
82
83
|
textPrompt(prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, cacheMessage?: boolean, maxOutputChars?: number): Promise<any>;
|
|
83
|
-
generateImage(prompt: string): Promise<{
|
|
84
|
+
generateImage(prompt: string, imageProperties?: Record<string, any>): Promise<{
|
|
84
85
|
artifactId?: string;
|
|
85
86
|
error?: any;
|
|
86
87
|
}>;
|
|
87
|
-
editImage(prompt: string, referenceImageArtifactIds?: string[]): Promise<{
|
|
88
|
+
editImage(prompt: string, imageProperties?: Record<string, any>, referenceImageArtifactIds?: string[]): Promise<{
|
|
88
89
|
artifactId?: string;
|
|
89
90
|
error?: any;
|
|
90
91
|
}>;
|