briyah 1.1.5 → 1.1.8
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/image_models.json +141 -21
- package/data/common/config/image_models_full.json +135 -48
- package/data/common/config/markup +1 -1
- package/data/common/config/story_models.json +41 -38
- package/data/common/prompts/character/progress_character.prompt +4 -0
- package/data/common/prompts/character/update_portrait.prompt +31 -0
- package/data/common/prompts/narrator/perceive.prompt +23 -1
- package/data/common/prompts/narrator/progress_plot.prompt +1 -3
- package/data/common/prompts/story_moderator/moderate.json +36 -29
- package/data/common/prompts/story_moderator/moderate.prompt +63 -18
- package/dist-sdk/server/src/ai/LLM/base-ai.service.d.ts +5 -7
- package/dist-sdk/server/src/ai/LLM/base-ai.service.js +7 -1
- package/dist-sdk/server/src/ai/LLM/fal.service.d.ts +5 -1
- package/dist-sdk/server/src/ai/LLM/fal.service.js +93 -21
- package/dist-sdk/server/src/ai/LLM/googleai.service.d.ts +1 -1
- package/dist-sdk/server/src/ai/LLM/googleai.service.js +20 -29
- package/dist-sdk/server/src/ai/LLM/openai.service.d.ts +5 -1
- package/dist-sdk/server/src/ai/LLM/openai.service.js +58 -71
- package/dist-sdk/server/src/ai/LLM/together.service.d.ts +5 -1
- package/dist-sdk/server/src/ai/LLM/together.service.js +57 -31
- package/dist-sdk/server/src/ai/agent-config.d.ts +1 -0
- package/dist-sdk/server/src/ai/agent-store.service.js +4 -0
- package/dist-sdk/server/src/ai/agent.d.ts +8 -1
- package/dist-sdk/server/src/ai/agent.js +21 -3
- package/dist-sdk/server/src/ai/model_prices.js +21 -1
- package/dist-sdk/server/src/app.controller.d.ts +4 -0
- package/dist-sdk/server/src/app.controller.js +51 -1
- package/dist-sdk/server/src/app.service.d.ts +3 -1
- package/dist-sdk/server/src/app.service.js +22 -14
- package/dist-sdk/server/src/room/message.d.ts +1 -0
- package/dist-sdk/server/src/room/message.js +1 -0
- package/dist-sdk/server/src/room/room-store.service.d.ts +1 -0
- package/dist-sdk/server/src/room/room-store.service.js +3 -0
- package/dist-sdk/server/src/room/room.d.ts +3 -0
- package/dist-sdk/server/src/room/room.js +27 -7
- 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-store.service.d.ts +1 -0
- package/dist-sdk/server/src/story/story-store.service.js +38 -0
- package/dist-sdk/server/src/story/story.service.d.ts +4 -1
- package/dist-sdk/server/src/story/story.service.js +233 -55
- package/dist-sdk/shared/types/app.types.d.ts +17 -0
- package/docs/assets/hierarchy.js +1 -1
- package/docs/assets/search.js +1 -1
- package/docs/classes/Agent.html +19 -15
- package/docs/classes/Briyah.html +12 -12
- package/docs/classes/BriyahConfigService.html +5 -5
- package/docs/classes/Room.html +26 -24
- package/docs/classes/RoomMessage.html +11 -10
- 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 +128 -113
- 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 +9 -6
- 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 +5 -5
- package/docs/interfaces/Message.html +2 -2
- package/docs/interfaces/ModelInfo.html +3 -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 +5 -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 +1 -1
|
@@ -53,6 +53,11 @@ This request will *not* become part of the readable story, only the narrator's r
|
|
|
53
53
|
3. Responder: The one character you choose to respond next to best maintain the flow of the conversation and further the story.
|
|
54
54
|
4. Targets: List of all characters who would realistically be able to hear the message (even if they shouldn't respond).
|
|
55
55
|
- When processing a message from the Narrator, the targets array should only include characters who are present for the situation being narrated.
|
|
56
|
+
5. 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.
|
|
57
|
+
- 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.
|
|
58
|
+
- Use the character's exact name as it appears in the Available Characters list.
|
|
59
|
+
- Leave the array empty if no visible appearance changes occurred.
|
|
60
|
+
- Minor or invisible changes (emotional state alone, unseen actions) do NOT warrant a portrait update.
|
|
56
61
|
|
|
57
62
|
## Content Moderation
|
|
58
63
|
- Do *not* allow profanity or swear words like 'hell', 'shit', and 'damn' or 'damned'.
|
|
@@ -67,7 +72,8 @@ You must respond with a valid JSON object in the following format:
|
|
|
67
72
|
"action": "relay", // or "relay_silent" for internal processing
|
|
68
73
|
"content": "relay", // or modified message body
|
|
69
74
|
"name": "CharacterName1", // Name of character who should respond next
|
|
70
|
-
"targets": ["CharacterName1", "CharacterName2"]
|
|
75
|
+
"targets": ["CharacterName1", "CharacterName2"], // List of all characters who would realistically be able to hear the message
|
|
76
|
+
"update_portrait": [] // Names of characters whose visible appearance changed; empty if none
|
|
71
77
|
}
|
|
72
78
|
|
|
73
79
|
## Conversation Rules
|
|
@@ -81,10 +87,10 @@ You must respond with a valid JSON object in the following format:
|
|
|
81
87
|
- **Important**:Avoid lengthy conversations that exclude the Human Character completely. Give the Human Character a chance to interact with the story.
|
|
82
88
|
|
|
83
89
|
## Introductions
|
|
84
|
-
- Only set "name" to the exact character name (nickname) of one of the characters in **Available Characters** list.
|
|
85
|
-
-
|
|
86
|
-
- Set
|
|
87
|
-
-
|
|
90
|
+
- Only set "name" to the exact character name (nickname) of one of the characters in the **Available Characters** list.
|
|
91
|
+
- 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.
|
|
92
|
+
- Set "name" to "Narrator" and "targets" to ["Narrator"].
|
|
93
|
+
- Use "action": "relay" so the dialog still becomes part of the readable story.
|
|
88
94
|
- If you receive a message from the system like "INTRODUCE [Character name]", relay the INTRODUCE [Character name] message to the Narrator so they may properly introduce the new character.
|
|
89
95
|
|
|
90
96
|
## Example Responses
|
|
@@ -105,7 +111,8 @@ Correct Response:
|
|
|
105
111
|
"action": "relay_silent",
|
|
106
112
|
"content": "PASS",
|
|
107
113
|
"name": "Narrator",
|
|
108
|
-
"targets": ["Narrator"]
|
|
114
|
+
"targets": ["Narrator"],
|
|
115
|
+
"update_portrait": []
|
|
109
116
|
}
|
|
110
117
|
- Mike is choosing not to act or speak, so select another appropriate character (or the narrator) to speak next instead.
|
|
111
118
|
- Use "relay_silent" because PASS is a meta-action and shouldn't appear in the story
|
|
@@ -118,7 +125,8 @@ Correct Response:
|
|
|
118
125
|
"action": "relay",
|
|
119
126
|
"content": "relay",
|
|
120
127
|
"name": "Mike",
|
|
121
|
-
"targets": ["Mike", "Bob"]
|
|
128
|
+
"targets": ["Mike", "Bob"],
|
|
129
|
+
"update_portrait": []
|
|
122
130
|
}
|
|
123
131
|
- According to the current situation, only Mike, Jessica, and Bob are within hearing distance of each other.
|
|
124
132
|
- Jessica is speaking, and only Mike and Bob can hear. Sergei and Carter are not present.
|
|
@@ -134,7 +142,8 @@ Correct Response:
|
|
|
134
142
|
"action": "relay",
|
|
135
143
|
"content": "relay",
|
|
136
144
|
"name": "Jessica",
|
|
137
|
-
"targets": ["Jessica", "Bob"]
|
|
145
|
+
"targets": ["Jessica", "Bob"],
|
|
146
|
+
"update_portrait": []
|
|
138
147
|
}
|
|
139
148
|
- According to the current situation, only Mike, Jessica, and Bob are within hearing distance of each other.
|
|
140
149
|
- Mike is speaking, and only Jessica and Bob can hear. Sergei and Carter are not present.
|
|
@@ -150,7 +159,8 @@ Correct Response:
|
|
|
150
159
|
"action": "relay_silent",
|
|
151
160
|
"content": "relay",
|
|
152
161
|
"name": "Narrator",
|
|
153
|
-
"targets": ["Narrator"]
|
|
162
|
+
"targets": ["Narrator"],
|
|
163
|
+
"update_portrait": []
|
|
154
164
|
}
|
|
155
165
|
- The sender is attempting to perform an action with the message
|
|
156
166
|
- Only the Narrator is able to handle the character's request to perform an action.
|
|
@@ -167,7 +177,8 @@ Correct Response:
|
|
|
167
177
|
"action": "relay",
|
|
168
178
|
"content": "relay",
|
|
169
179
|
"name": "Mike",
|
|
170
|
-
"targets": ["Mike", "Jessica", "Bob"]
|
|
180
|
+
"targets": ["Mike", "Jessica", "Bob"],
|
|
181
|
+
"update_portrait": []
|
|
171
182
|
}
|
|
172
183
|
- 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.
|
|
173
184
|
- All characters involved in the current situation should be targets of the message
|
|
@@ -182,25 +193,28 @@ Correct Response:
|
|
|
182
193
|
"action": "relay",
|
|
183
194
|
"content": "relay",
|
|
184
195
|
"name": "Sergei",
|
|
185
|
-
"targets": ["Sergei", "Carter"]
|
|
196
|
+
"targets": ["Sergei", "Carter"],
|
|
197
|
+
"update_portrait": []
|
|
186
198
|
}
|
|
187
199
|
- This is not dialog and there is no obvious responder, so choose an character involved in the current situation to be the responder
|
|
188
200
|
- All characters involved in the current situation should be targets of the message
|
|
189
201
|
- Mike, Jessica, and Bob should not be targets of this message because they are not involved in the situation.
|
|
190
202
|
|
|
191
203
|
|
|
192
|
-
### Example 6: Character addresses
|
|
204
|
+
### Example 6: Character addresses someone not listed in the Available Characters list
|
|
193
205
|
Recent Message Sender: Mike (human character)
|
|
194
206
|
Recent Message: Hello, Albert Flanagan!
|
|
195
207
|
Correct Response:
|
|
196
208
|
{
|
|
197
209
|
"action": "relay",
|
|
198
210
|
"content": "relay",
|
|
199
|
-
"name": "
|
|
200
|
-
"targets": ["
|
|
211
|
+
"name": "Narrator",
|
|
212
|
+
"targets": ["Narrator"],
|
|
213
|
+
"update_portrait": []
|
|
201
214
|
}
|
|
202
|
-
-
|
|
203
|
-
-
|
|
215
|
+
- "Albert Flanagan" is not in the Available Characters list.
|
|
216
|
+
- 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.
|
|
217
|
+
- Use "relay" so Mike's dialog still appears in the readable story.
|
|
204
218
|
|
|
205
219
|
### Example 7: Message contains explicit content
|
|
206
220
|
Recent Message Sender: Mike
|
|
@@ -210,7 +224,8 @@ Correct Response:
|
|
|
210
224
|
"action": "relay",
|
|
211
225
|
"content": "[message content modified to remove explicit content]",
|
|
212
226
|
"name": "Narrator",
|
|
213
|
-
"targets": ["Narrator"]
|
|
227
|
+
"targets": ["Narrator"],
|
|
228
|
+
"update_portrait": []
|
|
214
229
|
}
|
|
215
230
|
- If the message contains explicit content, just remove the explicit content if possible or modify the message to something more appropriate.
|
|
216
231
|
|
|
@@ -222,8 +237,38 @@ Correct Response:
|
|
|
222
237
|
"action": "relay_silent",
|
|
223
238
|
"content": "relay",
|
|
224
239
|
"name": "",
|
|
225
|
-
"targets": []
|
|
240
|
+
"targets": [],
|
|
241
|
+
"update_portrait": []
|
|
226
242
|
}
|
|
227
243
|
- Use the relay_silent action and leave the name field blank and set the targets list to an empty array.
|
|
228
244
|
- The message will be silently relayed to the Narrator to perform the introduction.
|
|
229
245
|
|
|
246
|
+
|
|
247
|
+
### Example 9: Narrator describes a visible appearance change
|
|
248
|
+
Recent Message Sender: Narrator
|
|
249
|
+
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.
|
|
250
|
+
Correct Response:
|
|
251
|
+
{
|
|
252
|
+
"action": "relay",
|
|
253
|
+
"content": "relay",
|
|
254
|
+
"name": "Mike",
|
|
255
|
+
"targets": ["Mike", "Jessica", "Bob"],
|
|
256
|
+
"update_portrait": ["Jessica"]
|
|
257
|
+
}
|
|
258
|
+
- Jessica's visible appearance has changed (torn sleeve, wound, makeshift bandage), so her portrait should be updated.
|
|
259
|
+
- All characters present in the current situation are targets.
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
### Example 10: Character action with no visible change
|
|
263
|
+
Recent Message Sender: Bob
|
|
264
|
+
Recent Message: Bob wields his new sword proudly above his head
|
|
265
|
+
Correct Response:
|
|
266
|
+
{
|
|
267
|
+
"action": "relay_silent",
|
|
268
|
+
"content": "relay",
|
|
269
|
+
"name": "Narrator",
|
|
270
|
+
"targets": ["Narrator"],
|
|
271
|
+
"update_portrait": ["Bob"]
|
|
272
|
+
}
|
|
273
|
+
- Bob just got a new weapon (or other visible item) that we would want to include in his portrait.
|
|
274
|
+
|
|
@@ -4,12 +4,6 @@ import { ArtifactService } from '../artifact.service';
|
|
|
4
4
|
import { AgentStoreService } from '../agent-store.service';
|
|
5
5
|
import { BalanceService } from '../../app/balance.service';
|
|
6
6
|
import { ModelInfo } from '../../../../shared/types/app.types';
|
|
7
|
-
export interface ImageGenerationOptions {
|
|
8
|
-
model?: string;
|
|
9
|
-
size?: '1024x1024' | '1536x1024' | '1024x1536' | 'auto';
|
|
10
|
-
quality?: 'auto' | 'high' | 'medium' | 'low';
|
|
11
|
-
format?: 'png' | 'jpeg' | 'webp';
|
|
12
|
-
}
|
|
13
7
|
export declare class BaseAiService {
|
|
14
8
|
protected modelsCache: ModelInfo[];
|
|
15
9
|
protected _isAvailable: boolean;
|
|
@@ -28,7 +22,11 @@ export declare class BaseAiService {
|
|
|
28
22
|
protected fetchModelsFromApi(): Promise<ModelInfo[]>;
|
|
29
23
|
createAgent(configService: ConfigurationService, agentStoreService: AgentStoreService, balanceService: BalanceService, artifactService: ArtifactService, agentName: string, agentNickname: string, description: string, promptFolder: string, modelName: string, storageDir?: string): Agent;
|
|
30
24
|
textPrompt(agent: Agent, prompt: string, _jsonSchema?: any, _saveResponse?: boolean, _promptInstructions?: string, _cacheConfig?: any, _maxOutputChars?: number): Promise<any>;
|
|
31
|
-
generateImage(_agent: Agent, _prompt: string,
|
|
25
|
+
generateImage(_agent: Agent, _prompt: string, _imageProperties?: Record<string, any>): Promise<{
|
|
26
|
+
artifactId?: string;
|
|
27
|
+
error?: any;
|
|
28
|
+
}>;
|
|
29
|
+
editImage(_agent: Agent, _prompt: string, _imageProperties?: Record<string, any>, _referenceImageArtifactIds?: string[]): Promise<{
|
|
32
30
|
artifactId?: string;
|
|
33
31
|
error?: any;
|
|
34
32
|
}>;
|
|
@@ -177,12 +177,18 @@ let BaseAiService = class BaseAiService {
|
|
|
177
177
|
}
|
|
178
178
|
throw new Error('Method not implemented in base class');
|
|
179
179
|
}
|
|
180
|
-
async generateImage(_agent, _prompt,
|
|
180
|
+
async generateImage(_agent, _prompt, _imageProperties) {
|
|
181
181
|
if (!this.isAvailable) {
|
|
182
182
|
return Promise.reject(new Error(`${this.getServiceName()} service is not available. Check API key configuration.`));
|
|
183
183
|
}
|
|
184
184
|
throw new Error(`generateImage method not implemented for ${this.getServiceName()}`);
|
|
185
185
|
}
|
|
186
|
+
async editImage(_agent, _prompt, _imageProperties, _referenceImageArtifactIds) {
|
|
187
|
+
if (!this.isAvailable) {
|
|
188
|
+
return Promise.reject(new Error(`${this.getServiceName()} service is not available. Check API key configuration.`));
|
|
189
|
+
}
|
|
190
|
+
throw new Error(`editImage method not implemented for ${this.getServiceName()}`);
|
|
191
|
+
}
|
|
186
192
|
async loadReferenceImages(agent, artifactIds) {
|
|
187
193
|
const images = [];
|
|
188
194
|
for (const artifactId of artifactIds) {
|
|
@@ -11,7 +11,11 @@ export declare class FalAiService extends BaseAiService {
|
|
|
11
11
|
textPrompt(agent: Agent, prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, _cacheConfig?: any, maxOutputChars?: number): Promise<any>;
|
|
12
12
|
private computeMessageCost;
|
|
13
13
|
private computeImageCost;
|
|
14
|
-
generateImage(agent: Agent, prompt: string,
|
|
14
|
+
generateImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>): Promise<{
|
|
15
|
+
artifactId?: string;
|
|
16
|
+
error?: any;
|
|
17
|
+
}>;
|
|
18
|
+
editImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>, referenceImageArtifactIds?: string[]): Promise<{
|
|
15
19
|
artifactId?: string;
|
|
16
20
|
error?: any;
|
|
17
21
|
}>;
|
|
@@ -218,25 +218,37 @@ let FalAiService = class FalAiService extends base_ai_service_1.BaseAiService {
|
|
|
218
218
|
agent.balanceService.decrementBalance(cost, markup);
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
|
-
computeImageCost(agent) {
|
|
221
|
+
computeImageCost(agent, centsPerImage) {
|
|
222
222
|
let modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
223
223
|
if (!modelInfo && !agent.modelName.startsWith('fal_ai/')) {
|
|
224
224
|
modelInfo = (0, model_prices_1.getModelPrices)()[`fal_ai/${agent.modelName}`];
|
|
225
225
|
}
|
|
226
|
-
|
|
227
|
-
|
|
226
|
+
let cost;
|
|
227
|
+
if (modelInfo?.output_cost_per_image != null) {
|
|
228
|
+
cost = modelInfo.output_cost_per_image;
|
|
229
|
+
}
|
|
230
|
+
else if (centsPerImage != null) {
|
|
231
|
+
cost = centsPerImage / 100;
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
logger_1.logger.warn(`No price info for model ${agent.modelName}. Skipping cost tracking.`);
|
|
228
235
|
return;
|
|
229
236
|
}
|
|
230
|
-
const cost = modelInfo.output_cost_per_image;
|
|
231
|
-
const markup = 0;
|
|
232
237
|
agent.totalCost += cost;
|
|
233
|
-
agent.
|
|
234
|
-
logger_1.logger.log(`Cost for image generation: ${cost.toFixed(4)} (markup: ${markup.toFixed(4)})`);
|
|
238
|
+
logger_1.logger.log(`Image cost for ${agent.agentName}: $${cost.toFixed(4)}`);
|
|
235
239
|
if (agent.balanceService && cost > 0) {
|
|
236
|
-
agent.balanceService.decrementBalance(cost,
|
|
240
|
+
agent.balanceService.decrementBalance(cost, 0);
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
|
-
async generateImage(agent, prompt,
|
|
243
|
+
async generateImage(agent, prompt, imageProperties) {
|
|
244
|
+
const { centsPerImage, ...apiProperties } = imageProperties ?? {};
|
|
245
|
+
let modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
246
|
+
if (!modelInfo && !agent.modelName.startsWith('fal_ai/')) {
|
|
247
|
+
modelInfo = (0, model_prices_1.getModelPrices)()[`fal_ai/${agent.modelName}`];
|
|
248
|
+
}
|
|
249
|
+
if (modelInfo?.output_cost_per_image == null && centsPerImage == null) {
|
|
250
|
+
return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
|
|
251
|
+
}
|
|
240
252
|
if (agent.balanceService && !agent.disableBalanceCheck) {
|
|
241
253
|
const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
|
|
242
254
|
if (!hasSufficientBalance) {
|
|
@@ -249,23 +261,14 @@ let FalAiService = class FalAiService extends base_ai_service_1.BaseAiService {
|
|
|
249
261
|
endpoint = `fal-ai/${endpoint}`;
|
|
250
262
|
}
|
|
251
263
|
const input = {
|
|
252
|
-
prompt
|
|
253
|
-
image_size: {
|
|
254
|
-
width: width,
|
|
255
|
-
height: height,
|
|
256
|
-
},
|
|
264
|
+
prompt,
|
|
257
265
|
num_images: 1,
|
|
258
|
-
|
|
266
|
+
...apiProperties,
|
|
259
267
|
};
|
|
260
|
-
if (referenceImageArtifactIds && referenceImageArtifactIds.length > 0) {
|
|
261
|
-
input.image_urls = await this.loadReferenceImages(agent, referenceImageArtifactIds);
|
|
262
|
-
if (!endpoint.endsWith('/edit'))
|
|
263
|
-
endpoint += '/edit';
|
|
264
|
-
}
|
|
265
268
|
const result = (await client_1.fal.subscribe(endpoint, {
|
|
266
269
|
input: input,
|
|
267
270
|
}));
|
|
268
|
-
this.computeImageCost(agent);
|
|
271
|
+
this.computeImageCost(agent, centsPerImage);
|
|
269
272
|
let base64Data;
|
|
270
273
|
if (result.data.images && result.data.images[0]) {
|
|
271
274
|
const image = result.data.images[0];
|
|
@@ -305,6 +308,75 @@ let FalAiService = class FalAiService extends base_ai_service_1.BaseAiService {
|
|
|
305
308
|
return { error };
|
|
306
309
|
}
|
|
307
310
|
}
|
|
311
|
+
async editImage(agent, prompt, imageProperties, referenceImageArtifactIds) {
|
|
312
|
+
const { centsPerImage, ...apiProperties } = imageProperties ?? {};
|
|
313
|
+
let modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
314
|
+
if (!modelInfo && !agent.modelName.startsWith('fal_ai/')) {
|
|
315
|
+
modelInfo = (0, model_prices_1.getModelPrices)()[`fal_ai/${agent.modelName}`];
|
|
316
|
+
}
|
|
317
|
+
if (modelInfo?.output_cost_per_image == null && centsPerImage == null) {
|
|
318
|
+
return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
|
|
319
|
+
}
|
|
320
|
+
if (agent.balanceService && !agent.disableBalanceCheck) {
|
|
321
|
+
const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
|
|
322
|
+
if (!hasSufficientBalance) {
|
|
323
|
+
throw new errors_1.InsufficientBalanceError('Insufficient balance for image generation. Please add funds to continue.');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
let endpoint = agent.modelName;
|
|
328
|
+
if (!endpoint.startsWith('fal-ai/')) {
|
|
329
|
+
endpoint = `fal-ai/${endpoint}`;
|
|
330
|
+
}
|
|
331
|
+
const input = {
|
|
332
|
+
prompt,
|
|
333
|
+
num_images: 1,
|
|
334
|
+
...apiProperties,
|
|
335
|
+
};
|
|
336
|
+
if (referenceImageArtifactIds && referenceImageArtifactIds.length > 0) {
|
|
337
|
+
input.image_urls = await this.loadReferenceImages(agent, referenceImageArtifactIds);
|
|
338
|
+
}
|
|
339
|
+
const result = (await client_1.fal.subscribe(endpoint, { input }));
|
|
340
|
+
this.computeImageCost(agent, centsPerImage);
|
|
341
|
+
let base64Data;
|
|
342
|
+
if (result.data.images && result.data.images[0]) {
|
|
343
|
+
const image = result.data.images[0];
|
|
344
|
+
if (image.url) {
|
|
345
|
+
const imageResponse = await fetch(image.url);
|
|
346
|
+
const imageBuffer = Buffer.from(await imageResponse.arrayBuffer());
|
|
347
|
+
base64Data = imageBuffer.toString('base64');
|
|
348
|
+
}
|
|
349
|
+
else if (image.content) {
|
|
350
|
+
base64Data = image.content;
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
throw new Error('No image data found in response');
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else if (result.data.image) {
|
|
357
|
+
if (result.data.image.url) {
|
|
358
|
+
const imageResponse = await fetch(result.data.image.url);
|
|
359
|
+
const imageBuffer = Buffer.from(await imageResponse.arrayBuffer());
|
|
360
|
+
base64Data = imageBuffer.toString('base64');
|
|
361
|
+
}
|
|
362
|
+
else if (result.data.image.content) {
|
|
363
|
+
base64Data = result.data.image.content;
|
|
364
|
+
}
|
|
365
|
+
else {
|
|
366
|
+
throw new Error('No image data found in response');
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
throw new Error('Unexpected response format from Fal AI');
|
|
371
|
+
}
|
|
372
|
+
const artifactId = await this.saveImageAsArtifact(agent, base64Data, prompt);
|
|
373
|
+
return { artifactId };
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
logger_1.logger.error('Error editing image with Fal AI:', error.message || error);
|
|
377
|
+
return { error };
|
|
378
|
+
}
|
|
379
|
+
}
|
|
308
380
|
};
|
|
309
381
|
exports.FalAiService = FalAiService;
|
|
310
382
|
exports.FalAiService = FalAiService = __decorate([
|
|
@@ -10,7 +10,7 @@ export declare class GoogleAiService extends BaseAiService {
|
|
|
10
10
|
fetchModelsFromApi(): Promise<ModelInfo[]>;
|
|
11
11
|
addToConversationHistory(agent: Agent, message: string, fromSelf?: boolean): void;
|
|
12
12
|
textPrompt(agent: Agent, prompt: string, jsonSchema?: any, saveResponse?: boolean, promptInstructions?: string, _cacheConfig?: any, maxOutputChars?: number): Promise<any>;
|
|
13
|
-
generateImage(agent: Agent, prompt: string,
|
|
13
|
+
generateImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>): Promise<{
|
|
14
14
|
artifactId?: string;
|
|
15
15
|
error?: any;
|
|
16
16
|
}>;
|
|
@@ -153,7 +153,12 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
|
|
|
153
153
|
}
|
|
154
154
|
return responseText;
|
|
155
155
|
}
|
|
156
|
-
async generateImage(agent, prompt,
|
|
156
|
+
async generateImage(agent, prompt, imageProperties) {
|
|
157
|
+
const { centsPerImage, ...apiProperties } = imageProperties ?? {};
|
|
158
|
+
const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
159
|
+
if ((!modelInfo || !modelInfo.output_cost_per_image) && centsPerImage == null) {
|
|
160
|
+
return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
|
|
161
|
+
}
|
|
157
162
|
if (agent.balanceService && !agent.disableBalanceCheck) {
|
|
158
163
|
const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
|
|
159
164
|
if (!hasSufficientBalance) {
|
|
@@ -161,32 +166,10 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
|
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
try {
|
|
164
|
-
const modelName = agent.modelName;
|
|
165
|
-
let aspectRatio = '1:1';
|
|
166
|
-
if (width && height) {
|
|
167
|
-
const ratio = width / height;
|
|
168
|
-
if (ratio > 1.4)
|
|
169
|
-
aspectRatio = '16:9';
|
|
170
|
-
else if (ratio > 1.2)
|
|
171
|
-
aspectRatio = '3:2';
|
|
172
|
-
else if (ratio < 0.7)
|
|
173
|
-
aspectRatio = '9:16';
|
|
174
|
-
else if (ratio < 0.8)
|
|
175
|
-
aspectRatio = '2:3';
|
|
176
|
-
}
|
|
177
|
-
const config = {
|
|
178
|
-
numberOfImages: 1,
|
|
179
|
-
aspectRatio: aspectRatio,
|
|
180
|
-
};
|
|
181
|
-
if (quality && quality !== 'auto') {
|
|
182
|
-
config.outputOptions = {
|
|
183
|
-
compressionQuality: quality === 'high' ? 90 : quality === 'medium' ? 70 : 50,
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
169
|
const response = await this.googleAI.models.generateImages({
|
|
187
|
-
model: modelName,
|
|
188
|
-
prompt
|
|
189
|
-
config:
|
|
170
|
+
model: agent.modelName,
|
|
171
|
+
prompt,
|
|
172
|
+
config: { ...apiProperties },
|
|
190
173
|
});
|
|
191
174
|
let base64Data;
|
|
192
175
|
if (response.generatedImages?.[0]?.image?.imageBytes) {
|
|
@@ -198,7 +181,7 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
|
|
|
198
181
|
else {
|
|
199
182
|
throw new Error('No image data in response');
|
|
200
183
|
}
|
|
201
|
-
this.computeImageCost(agent, modelName);
|
|
184
|
+
this.computeImageCost(agent, agent.modelName, centsPerImage);
|
|
202
185
|
const artifactId = await this.saveImageAsArtifact(agent, base64Data, prompt);
|
|
203
186
|
return { artifactId };
|
|
204
187
|
}
|
|
@@ -228,10 +211,18 @@ let GoogleAiService = class GoogleAiService extends base_ai_service_1.BaseAiServ
|
|
|
228
211
|
agent.balanceService.decrementBalance(cost, markup);
|
|
229
212
|
}
|
|
230
213
|
}
|
|
231
|
-
computeImageCost(agent, modelName) {
|
|
214
|
+
computeImageCost(agent, modelName, centsPerImage) {
|
|
232
215
|
const modelInfo = (0, model_prices_1.getModelPrices)()[modelName];
|
|
233
216
|
if (!modelInfo || !modelInfo.output_cost_per_image) {
|
|
234
|
-
|
|
217
|
+
if (centsPerImage != null) {
|
|
218
|
+
const cost = centsPerImage / 100;
|
|
219
|
+
agent.totalCost += cost;
|
|
220
|
+
logger_1.logger.log(`Image cost for ${agent.agentName}: $${cost.toFixed(4)}`);
|
|
221
|
+
if (agent.balanceService && cost > 0)
|
|
222
|
+
agent.balanceService.decrementBalance(cost, 0);
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
logger_1.logger.warn(`No image price info found for model ${modelName}. Skipping cost tracking.`);
|
|
235
226
|
return;
|
|
236
227
|
}
|
|
237
228
|
const outputCost = modelInfo.output_cost_per_image;
|
|
@@ -14,7 +14,11 @@ export declare class OpenAiService extends BaseAiService {
|
|
|
14
14
|
addToConversationHistory(agent: Agent, message: string, fromSelf?: boolean, developer?: boolean): void;
|
|
15
15
|
private computeMessageCost;
|
|
16
16
|
private computeImageCost;
|
|
17
|
-
generateImage(agent: Agent, prompt: string,
|
|
17
|
+
generateImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>): Promise<{
|
|
18
|
+
artifactId?: string;
|
|
19
|
+
error?: any;
|
|
20
|
+
}>;
|
|
21
|
+
editImage(agent: Agent, prompt: string, imageProperties?: Record<string, any>, referenceImageArtifactIds?: string[]): Promise<{
|
|
18
22
|
artifactId?: string;
|
|
19
23
|
error?: any;
|
|
20
24
|
}>;
|
|
@@ -315,60 +315,31 @@ let OpenAiService = class OpenAiService extends base_ai_service_1.BaseAiService
|
|
|
315
315
|
agent.balanceService.decrementBalance(cost, markup);
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
|
-
computeImageCost(agent,
|
|
318
|
+
computeImageCost(agent, centsPerImage) {
|
|
319
319
|
const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
let cost;
|
|
321
|
+
if (modelInfo?.output_cost_per_image != null) {
|
|
322
|
+
cost = modelInfo.output_cost_per_image;
|
|
323
323
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
const outputImageTokens = usage.output_tokens_details?.image_tokens || 0;
|
|
327
|
-
const outputTextTokens = usage.output_tokens_details?.text_tokens || 0;
|
|
328
|
-
const totalInputTokens = usage.input_tokens || 0;
|
|
329
|
-
const totalOutputTokens = usage.output_tokens || 0;
|
|
330
|
-
agent.totalInputTokens += totalInputTokens;
|
|
331
|
-
agent.totalOutputTokens += totalOutputTokens;
|
|
332
|
-
let inputCost = 0;
|
|
333
|
-
if (inputImageTokens > 0) {
|
|
334
|
-
const cachedImageTokens = usage.input_tokens_details?.cached_image_tokens || 0;
|
|
335
|
-
const uncachedImageTokens = inputImageTokens - cachedImageTokens;
|
|
336
|
-
inputCost += uncachedImageTokens * (modelInfo.input_cost_per_image_token || 0);
|
|
337
|
-
if (cachedImageTokens > 0) {
|
|
338
|
-
inputCost +=
|
|
339
|
-
cachedImageTokens *
|
|
340
|
-
(modelInfo.cache_read_input_image_token_cost ||
|
|
341
|
-
modelInfo.input_cost_per_image_token * 0.5);
|
|
342
|
-
}
|
|
324
|
+
else if (centsPerImage != null) {
|
|
325
|
+
cost = centsPerImage / 100;
|
|
343
326
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
inputCost += uncachedTextTokens * (modelInfo.input_cost_per_token || 0);
|
|
348
|
-
if (cachedTextTokens > 0) {
|
|
349
|
-
inputCost +=
|
|
350
|
-
cachedTextTokens *
|
|
351
|
-
(modelInfo.cache_read_input_text_token_cost ||
|
|
352
|
-
modelInfo.input_cost_per_token * 0.5);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
let outputCost = 0;
|
|
356
|
-
if (outputImageTokens > 0) {
|
|
357
|
-
outputCost += outputImageTokens * (modelInfo.output_cost_per_image_token || 0);
|
|
358
|
-
}
|
|
359
|
-
if (outputTextTokens > 0) {
|
|
360
|
-
outputCost += outputTextTokens * (modelInfo.output_cost_per_token || 0);
|
|
327
|
+
else {
|
|
328
|
+
logger_1.logger.warn(`No price info for model ${agent.modelName}. Skipping cost tracking.`);
|
|
329
|
+
return;
|
|
361
330
|
}
|
|
362
|
-
const markup = outputCost * 0;
|
|
363
|
-
const cost = inputCost + outputCost + markup;
|
|
364
331
|
agent.totalCost += cost;
|
|
365
|
-
agent.
|
|
366
|
-
logger_1.logger.log(`Cost for image generation: ${cost} (markup: ${markup.toFixed(4)})`);
|
|
332
|
+
logger_1.logger.log(`Image cost for ${agent.agentName}: $${cost.toFixed(4)}`);
|
|
367
333
|
if (agent.balanceService && cost > 0) {
|
|
368
|
-
agent.balanceService.decrementBalance(cost,
|
|
334
|
+
agent.balanceService.decrementBalance(cost, 0);
|
|
369
335
|
}
|
|
370
336
|
}
|
|
371
|
-
async generateImage(agent, prompt,
|
|
337
|
+
async generateImage(agent, prompt, imageProperties) {
|
|
338
|
+
const { centsPerImage, ...apiProperties } = imageProperties ?? {};
|
|
339
|
+
const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
340
|
+
if (modelInfo?.output_cost_per_image == null && centsPerImage == null) {
|
|
341
|
+
return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
|
|
342
|
+
}
|
|
372
343
|
if (agent.balanceService) {
|
|
373
344
|
const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
|
|
374
345
|
if (!hasSufficientBalance) {
|
|
@@ -376,31 +347,13 @@ let OpenAiService = class OpenAiService extends base_ai_service_1.BaseAiService
|
|
|
376
347
|
}
|
|
377
348
|
}
|
|
378
349
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
image: referenceImages.length === 1 ? referenceImages[0] : referenceImages,
|
|
387
|
-
size: size,
|
|
388
|
-
n: 1,
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
else {
|
|
392
|
-
imageResponse = await this.openai.images.generate({
|
|
393
|
-
model: agent.modelName,
|
|
394
|
-
prompt: prompt,
|
|
395
|
-
n: 1,
|
|
396
|
-
size: size,
|
|
397
|
-
quality: quality || 'auto',
|
|
398
|
-
moderation: 'low',
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
if (imageResponse.usage) {
|
|
402
|
-
this.computeImageCost(agent, imageResponse.usage);
|
|
403
|
-
}
|
|
350
|
+
const imageResponse = await this.openai.images.generate({
|
|
351
|
+
model: agent.modelName,
|
|
352
|
+
prompt,
|
|
353
|
+
n: 1,
|
|
354
|
+
...apiProperties,
|
|
355
|
+
});
|
|
356
|
+
this.computeImageCost(agent, centsPerImage);
|
|
404
357
|
const base64Data = imageResponse.data[0].b64_json;
|
|
405
358
|
const artifactId = await this.saveImageAsArtifact(agent, base64Data, prompt);
|
|
406
359
|
return { artifactId };
|
|
@@ -411,6 +364,40 @@ let OpenAiService = class OpenAiService extends base_ai_service_1.BaseAiService
|
|
|
411
364
|
return { error };
|
|
412
365
|
}
|
|
413
366
|
}
|
|
367
|
+
async editImage(agent, prompt, imageProperties, referenceImageArtifactIds) {
|
|
368
|
+
const { centsPerImage, ...apiProperties } = imageProperties ?? {};
|
|
369
|
+
const modelInfo = (0, model_prices_1.getModelPrices)()[agent.modelName];
|
|
370
|
+
if (modelInfo?.output_cost_per_image == null && centsPerImage == null) {
|
|
371
|
+
return { error: `No pricing configured for image model "${agent.modelName}". Add a modelPrices entry or set centsPerImage in image_models.json.` };
|
|
372
|
+
}
|
|
373
|
+
if (agent.balanceService) {
|
|
374
|
+
const hasSufficientBalance = agent.balanceService.hasSufficientBalance();
|
|
375
|
+
if (!hasSufficientBalance) {
|
|
376
|
+
throw new errors_1.InsufficientBalanceError('Insufficient balance for image generation. Please add funds to continue.');
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
const referenceImages = referenceImageArtifactIds?.length
|
|
381
|
+
? await this.loadReferenceImages(agent, referenceImageArtifactIds)
|
|
382
|
+
: [];
|
|
383
|
+
const imageResponse = await this.openai.images.edit({
|
|
384
|
+
model: agent.modelName,
|
|
385
|
+
prompt,
|
|
386
|
+
image: referenceImages.length === 1 ? referenceImages[0] : referenceImages,
|
|
387
|
+
n: 1,
|
|
388
|
+
...apiProperties,
|
|
389
|
+
});
|
|
390
|
+
this.computeImageCost(agent, centsPerImage);
|
|
391
|
+
const base64Data = imageResponse.data[0].b64_json;
|
|
392
|
+
const artifactId = await this.saveImageAsArtifact(agent, base64Data, prompt);
|
|
393
|
+
return { artifactId };
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
logger_1.logger.error('Error editing image:', error);
|
|
397
|
+
logger_1.logger.log(prompt);
|
|
398
|
+
return { error };
|
|
399
|
+
}
|
|
400
|
+
}
|
|
414
401
|
};
|
|
415
402
|
exports.OpenAiService = OpenAiService;
|
|
416
403
|
exports.OpenAiService = OpenAiService = __decorate([
|