@zodic/shared 0.0.148 → 0.0.150
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/app/api/index.ts +63 -0
- package/app/services/ConceptService.ts +47 -36
- package/package.json +1 -1
package/app/api/index.ts
CHANGED
|
@@ -28,7 +28,70 @@ const deepseek = (env: BackendBindings) =>
|
|
|
28
28
|
baseURL: 'https://api.deepseek.com/v1',
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
+
const together = (env: BackendBindings) =>
|
|
32
|
+
new OpenAI({
|
|
33
|
+
apiKey: env.TOGETHER_API_KEY,
|
|
34
|
+
baseURL: 'https://api.together.xyz/v1',
|
|
35
|
+
});
|
|
36
|
+
|
|
31
37
|
export const Api = (env: BackendBindings) => ({
|
|
38
|
+
callTogether: {
|
|
39
|
+
single: async (
|
|
40
|
+
messages: ChatMessages,
|
|
41
|
+
{ model = 'deepseek-ai/DeepSeek-V3', options = {} }: DeepSeekOptions
|
|
42
|
+
): Promise<string> => {
|
|
43
|
+
try {
|
|
44
|
+
const response = await together(env).chat.completions.create({
|
|
45
|
+
model,
|
|
46
|
+
messages,
|
|
47
|
+
...options,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
if (response.choices[0].message.content) {
|
|
51
|
+
return response.choices[0].message.content.trim();
|
|
52
|
+
} else {
|
|
53
|
+
throw new Error('Content is null');
|
|
54
|
+
}
|
|
55
|
+
} catch (err: any) {
|
|
56
|
+
console.error('Error calling DeepSeek API:', err.message);
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
// batch: async (
|
|
62
|
+
// batchItems: DeepSeekBatchInputItem[]
|
|
63
|
+
// ): Promise<DeepSeekBatchResponse> => {
|
|
64
|
+
// console.log('Preparing to send DeepSeek batch request.');
|
|
65
|
+
|
|
66
|
+
// try {
|
|
67
|
+
// const responses: DeepSeekBatchResponse = [];
|
|
68
|
+
|
|
69
|
+
// for (const item of batchItems) {
|
|
70
|
+
// const { messages, options } = item;
|
|
71
|
+
// const response = await deepseek(env).chat.completions.create({
|
|
72
|
+
// model: options?.model || 'deepseek-v3',
|
|
73
|
+
// messages,
|
|
74
|
+
// ...options,
|
|
75
|
+
// });
|
|
76
|
+
|
|
77
|
+
// if (response.choices[0].message.content) {
|
|
78
|
+
// responses.push({
|
|
79
|
+
// input: item,
|
|
80
|
+
// output: response.choices[0].message.content.trim(),
|
|
81
|
+
// });
|
|
82
|
+
// } else {
|
|
83
|
+
// console.error('Content is null');
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
|
+
|
|
87
|
+
// console.log('Batch processing completed successfully.');
|
|
88
|
+
// return responses;
|
|
89
|
+
// } catch (error) {
|
|
90
|
+
// console.error('Error in callDeepSeekBatch:', error);
|
|
91
|
+
// throw error;
|
|
92
|
+
// }
|
|
93
|
+
// },
|
|
94
|
+
},
|
|
32
95
|
callDeepSeek: {
|
|
33
96
|
single: async (
|
|
34
97
|
messages: ChatMessages,
|
|
@@ -77,7 +77,7 @@ export class ConceptService {
|
|
|
77
77
|
// ✅ Call ChatGPT API
|
|
78
78
|
const response = await this.context
|
|
79
79
|
.api()
|
|
80
|
-
.
|
|
80
|
+
.callTogether.single(messages, {});
|
|
81
81
|
if (!response) {
|
|
82
82
|
throw new Error(`❌ AI returned an empty response`);
|
|
83
83
|
}
|
|
@@ -155,34 +155,38 @@ export class ConceptService {
|
|
|
155
155
|
poemPT: string[];
|
|
156
156
|
} {
|
|
157
157
|
console.log('📌 Parsing basic info response from ChatGPT:', response);
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
const enMatch = response.match(
|
|
160
160
|
/EN:\s*•\s*Name:\s*(.+?)\s*•\s*Description:\s*([\s\S]+?)\s*•\s*Poetic Passage:\s*([\s\S]+?)\s*(?=PT:|$)/
|
|
161
161
|
);
|
|
162
162
|
const ptMatch = response.match(
|
|
163
163
|
/PT:\s*•\s*Nome:\s*(.+?)\s*•\s*Descrição:\s*([\s\S]+?)\s*•\s*Passagem Poética:\s*([\s\S]+)/
|
|
164
164
|
);
|
|
165
|
-
|
|
165
|
+
|
|
166
166
|
if (!enMatch || !ptMatch) {
|
|
167
167
|
console.error('❌ Invalid basic info response format:', response);
|
|
168
168
|
throw new Error('Invalid basic info response format');
|
|
169
169
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
const
|
|
170
|
+
|
|
171
|
+
// ✅ Function to clean text (removes leading/trailing spaces & unwanted "*")
|
|
172
|
+
const cleanText = (text: string) => text.trim().replace(/\*/g, '');
|
|
173
|
+
|
|
174
|
+
// ✅ Parse and clean extracted content
|
|
175
|
+
const nameEN = cleanText(enMatch[1]);
|
|
176
|
+
const descriptionEN = cleanText(enMatch[2]);
|
|
173
177
|
const poemEN = enMatch[3]
|
|
174
178
|
.trim()
|
|
175
179
|
.split(/\n+/)
|
|
176
|
-
.map((line) => line
|
|
177
|
-
|
|
178
|
-
const namePT = ptMatch[1]
|
|
179
|
-
const descriptionPT = ptMatch[2]
|
|
180
|
+
.map((line) => cleanText(line)); // ✅ Split into array & clean lines
|
|
181
|
+
|
|
182
|
+
const namePT = cleanText(ptMatch[1]);
|
|
183
|
+
const descriptionPT = cleanText(ptMatch[2]);
|
|
180
184
|
const poemPT = ptMatch[3]
|
|
181
185
|
.trim()
|
|
182
186
|
.split(/\n+/)
|
|
183
|
-
.map((line) => line
|
|
184
|
-
|
|
185
|
-
console.log('✅ Successfully parsed basic info:', {
|
|
187
|
+
.map((line) => cleanText(line)); // ✅ Split into array & clean lines
|
|
188
|
+
|
|
189
|
+
console.log('✅ Successfully parsed and cleaned basic info:', {
|
|
186
190
|
nameEN,
|
|
187
191
|
descriptionEN,
|
|
188
192
|
poemEN,
|
|
@@ -190,7 +194,7 @@ export class ConceptService {
|
|
|
190
194
|
descriptionPT,
|
|
191
195
|
poemPT,
|
|
192
196
|
});
|
|
193
|
-
|
|
197
|
+
|
|
194
198
|
return { nameEN, descriptionEN, poemEN, namePT, descriptionPT, poemPT };
|
|
195
199
|
}
|
|
196
200
|
|
|
@@ -239,7 +243,7 @@ export class ConceptService {
|
|
|
239
243
|
});
|
|
240
244
|
|
|
241
245
|
// ✅ Call ChatGPT API
|
|
242
|
-
const response = await this.context.api().
|
|
246
|
+
const response = await this.context.api().callTogether.single(messages, {});
|
|
243
247
|
if (!response) {
|
|
244
248
|
throw new Error(
|
|
245
249
|
`❌ Failed to generate Leonardo prompt for concept: ${conceptSlug}`
|
|
@@ -327,7 +331,7 @@ export class ConceptService {
|
|
|
327
331
|
// ✅ Call ChatGPT API
|
|
328
332
|
const response = await this.context
|
|
329
333
|
.api()
|
|
330
|
-
.
|
|
334
|
+
.callTogether.single(messages, {});
|
|
331
335
|
if (!response) {
|
|
332
336
|
throw new Error(`❌ AI returned an empty response`);
|
|
333
337
|
}
|
|
@@ -388,19 +392,16 @@ export class ConceptService {
|
|
|
388
392
|
structuredContentEN: StructuredConceptContent;
|
|
389
393
|
structuredContentPT: StructuredConceptContent;
|
|
390
394
|
} {
|
|
391
|
-
console.log(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
const sections = [
|
|
395
|
+
console.log('📌 Parsing structured content from ChatGPT response:', response);
|
|
396
|
+
|
|
397
|
+
const sectionsEN = [
|
|
397
398
|
'Core Identity',
|
|
398
399
|
'Strengths and Challenges',
|
|
399
400
|
'Path to Fulfillment',
|
|
400
401
|
'Emotional Depth',
|
|
401
402
|
'Vision and Aspirations',
|
|
402
403
|
];
|
|
403
|
-
|
|
404
|
+
|
|
404
405
|
const sectionsPT = [
|
|
405
406
|
'Identidade Essencial',
|
|
406
407
|
'Forças e Desafios',
|
|
@@ -408,39 +409,49 @@ export class ConceptService {
|
|
|
408
409
|
'Profundidade Emocional',
|
|
409
410
|
'Visão e Aspirações',
|
|
410
411
|
];
|
|
411
|
-
|
|
412
|
+
|
|
412
413
|
// ✅ Match English and Portuguese content separately
|
|
413
414
|
const enMatches = response.match(/EN:\s*([\s\S]+?)\s*PT:/);
|
|
414
415
|
const ptMatches = response.match(/PT:\s*([\s\S]+)/);
|
|
415
|
-
|
|
416
|
+
|
|
416
417
|
if (!enMatches || !ptMatches) {
|
|
418
|
+
console.error('❌ Missing English or Portuguese content in response.');
|
|
417
419
|
throw new Error('❌ Missing English or Portuguese content in response.');
|
|
418
420
|
}
|
|
419
|
-
|
|
421
|
+
|
|
420
422
|
const enContent = enMatches[1].trim();
|
|
421
423
|
const ptContent = ptMatches[1].trim();
|
|
422
|
-
|
|
424
|
+
|
|
425
|
+
// ✅ Function to clean AI artifacts (removes * and trims text)
|
|
426
|
+
function cleanText(text: string): string {
|
|
427
|
+
return text.replace(/\*/g, '').trim();
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ✅ Function to extract structured sections from text
|
|
423
431
|
function extractSections(text: string, sectionTitles: string[]) {
|
|
424
432
|
return sectionTitles.map((title) => {
|
|
425
|
-
|
|
433
|
+
// ✅ Improved regex: Ensures section detection even with different AI formatting
|
|
434
|
+
const regex = new RegExp(`${title}:\\s*([\\s\\S]+?)(?=\\n\\d|\\n[A-Z]|$)`);
|
|
426
435
|
const match = text.match(regex);
|
|
427
|
-
|
|
436
|
+
|
|
428
437
|
if (!match) {
|
|
438
|
+
console.warn(`⚠️ Missing section: ${title}`);
|
|
429
439
|
return { type: 'section', title, content: ['❌ Section Missing'] };
|
|
430
440
|
}
|
|
431
|
-
|
|
432
|
-
// ✅ Split content into paragraphs
|
|
441
|
+
|
|
442
|
+
// ✅ Split content into paragraphs and clean them
|
|
433
443
|
const paragraphs = match[1]
|
|
434
444
|
.trim()
|
|
435
|
-
.split(/\n
|
|
436
|
-
.map((p) => p
|
|
437
|
-
|
|
445
|
+
.split(/\n{2,}/) // Handles cases where paragraphs are separated by multiple new lines
|
|
446
|
+
.map((p) => cleanText(p));
|
|
447
|
+
|
|
438
448
|
return { type: 'section', title, content: paragraphs };
|
|
439
449
|
});
|
|
440
450
|
}
|
|
441
|
-
|
|
451
|
+
|
|
452
|
+
// ✅ Return parsed and cleaned structured content
|
|
442
453
|
return {
|
|
443
|
-
structuredContentEN: extractSections(enContent,
|
|
454
|
+
structuredContentEN: extractSections(enContent, sectionsEN),
|
|
444
455
|
structuredContentPT: extractSections(ptContent, sectionsPT),
|
|
445
456
|
};
|
|
446
457
|
}
|