coaiajs 0.1.0

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.
Files changed (162) hide show
  1. package/CLAUDE.md +56 -0
  2. package/KINSHIP.md +63 -0
  3. package/README.md +148 -0
  4. package/articles/academic/creative-orientation-vs-problem-solving.md +177 -0
  5. package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +142 -0
  6. package/articles/academic/langfuse-observability-llm-pipelines.md +144 -0
  7. package/articles/academic/medicine-wheel-software-architecture.md +163 -0
  8. package/articles/academic/mmot-autonomous-agents.md +156 -0
  9. package/articles/academic/model-context-protocol-interagent.md +161 -0
  10. package/articles/academic/pde-prompt-decomposition.md +186 -0
  11. package/articles/academic/structural-tension-in-ai-agents.md +134 -0
  12. package/articles/reviews/mcp-protocol-design-review.md +170 -0
  13. package/articles/reviews/observability-ai-systems-review.md +176 -0
  14. package/articles/reviews/prompt-engineering-decomposition-review.md +184 -0
  15. package/articles/surveys/agent-orchestration-survey.md +186 -0
  16. package/articles/surveys/knowledge-graph-storage-survey.md +204 -0
  17. package/articles/surveys/structural-tension-methodology-survey.md +154 -0
  18. package/articles/technical/aws-sdk-v3-polly.md +270 -0
  19. package/articles/technical/commander-cli-framework.md +262 -0
  20. package/articles/technical/dotenv-config-patterns.md +360 -0
  21. package/articles/technical/ioredis-vs-redis.md +142 -0
  22. package/articles/technical/langfuse-js-sdk-vs-rest.md +191 -0
  23. package/articles/technical/mcp-sdk-typescript.md +291 -0
  24. package/articles/technical/octokit-github-api.md +293 -0
  25. package/articles/technical/openai-sdk-modern.md +231 -0
  26. package/articles/technical/yaml-parsing-node.md +266 -0
  27. package/articles/technical/zod-runtime-validation.md +212 -0
  28. package/dist/mcp/config.d.ts +14 -0
  29. package/dist/mcp/config.js +185 -0
  30. package/dist/mcp/server.d.ts +3 -0
  31. package/dist/mcp/server.js +324 -0
  32. package/dist/mcp/tools/coaiapy-tools.d.ts +8 -0
  33. package/dist/mcp/tools/coaiapy-tools.js +326 -0
  34. package/dist/mcp/tools/index.d.ts +3 -0
  35. package/dist/mcp/tools/index.js +3 -0
  36. package/dist/src/audio.d.ts +12 -0
  37. package/dist/src/audio.js +57 -0
  38. package/dist/src/cli-helpers.d.ts +9 -0
  39. package/dist/src/cli-helpers.js +78 -0
  40. package/dist/src/cli.d.ts +3 -0
  41. package/dist/src/cli.js +1086 -0
  42. package/dist/src/config.d.ts +15 -0
  43. package/dist/src/config.js +145 -0
  44. package/dist/src/environment.d.ts +44 -0
  45. package/dist/src/environment.js +146 -0
  46. package/dist/src/github.d.ts +47 -0
  47. package/dist/src/github.js +79 -0
  48. package/dist/src/langfuse/client.d.ts +30 -0
  49. package/dist/src/langfuse/client.js +75 -0
  50. package/dist/src/langfuse/comments.d.ts +16 -0
  51. package/dist/src/langfuse/comments.js +36 -0
  52. package/dist/src/langfuse/datasets.d.ts +19 -0
  53. package/dist/src/langfuse/datasets.js +140 -0
  54. package/dist/src/langfuse/index.d.ts +13 -0
  55. package/dist/src/langfuse/index.js +10 -0
  56. package/dist/src/langfuse/media.d.ts +11 -0
  57. package/dist/src/langfuse/media.js +167 -0
  58. package/dist/src/langfuse/observations.d.ts +18 -0
  59. package/dist/src/langfuse/observations.js +113 -0
  60. package/dist/src/langfuse/prompts.d.ts +14 -0
  61. package/dist/src/langfuse/prompts.js +127 -0
  62. package/dist/src/langfuse/scores.d.ts +54 -0
  63. package/dist/src/langfuse/scores.js +366 -0
  64. package/dist/src/langfuse/traces.d.ts +29 -0
  65. package/dist/src/langfuse/traces.js +225 -0
  66. package/dist/src/llm.d.ts +23 -0
  67. package/dist/src/llm.js +76 -0
  68. package/dist/src/narrative/graph-manager.d.ts +106 -0
  69. package/dist/src/narrative/graph-manager.js +915 -0
  70. package/dist/src/narrative/index.d.ts +9 -0
  71. package/dist/src/narrative/index.js +7 -0
  72. package/dist/src/narrative/markdown-export.d.ts +21 -0
  73. package/dist/src/narrative/markdown-export.js +383 -0
  74. package/dist/src/narrative/tool-definitions.d.ts +21 -0
  75. package/dist/src/narrative/tool-definitions.js +588 -0
  76. package/dist/src/narrative/tool-handlers.d.ts +11 -0
  77. package/dist/src/narrative/tool-handlers.js +462 -0
  78. package/dist/src/narrative/types.d.ts +2 -0
  79. package/dist/src/narrative/types.js +2 -0
  80. package/dist/src/narrative/validation.d.ts +29 -0
  81. package/dist/src/narrative/validation.js +144 -0
  82. package/dist/src/pde/index.d.ts +8 -0
  83. package/dist/src/pde/index.js +8 -0
  84. package/dist/src/pde/mcp-handlers.d.ts +7 -0
  85. package/dist/src/pde/mcp-handlers.js +300 -0
  86. package/dist/src/pde/mcp-tools.d.ts +15 -0
  87. package/dist/src/pde/mcp-tools.js +191 -0
  88. package/dist/src/pde/session-manager.d.ts +32 -0
  89. package/dist/src/pde/session-manager.js +205 -0
  90. package/dist/src/pde/stc-mapper.d.ts +26 -0
  91. package/dist/src/pde/stc-mapper.js +234 -0
  92. package/dist/src/pipeline/index.d.ts +2 -0
  93. package/dist/src/pipeline/index.js +3 -0
  94. package/dist/src/pipeline/template-engine.d.ts +20 -0
  95. package/dist/src/pipeline/template-engine.js +361 -0
  96. package/dist/src/planning/index.d.ts +7 -0
  97. package/dist/src/planning/index.js +7 -0
  98. package/dist/src/planning/mcp-handlers.d.ts +7 -0
  99. package/dist/src/planning/mcp-handlers.js +310 -0
  100. package/dist/src/planning/mcp-tools.d.ts +15 -0
  101. package/dist/src/planning/mcp-tools.js +145 -0
  102. package/dist/src/planning/plan-parser.d.ts +35 -0
  103. package/dist/src/planning/plan-parser.js +506 -0
  104. package/dist/src/redis.d.ts +21 -0
  105. package/dist/src/redis.js +78 -0
  106. package/dist/src/types.d.ts +244 -0
  107. package/dist/src/types.js +8 -0
  108. package/mcp/config.ts +196 -0
  109. package/mcp/server.ts +402 -0
  110. package/mcp/tools/coaiapy-tools.ts +364 -0
  111. package/mcp/tools/index.ts +4 -0
  112. package/package.json +52 -0
  113. package/rispecs/00-coaiajs-platform.spec.md +102 -0
  114. package/rispecs/01-core-config.spec.md +89 -0
  115. package/rispecs/02-redis-module.spec.md +62 -0
  116. package/rispecs/03-langfuse-module.spec.md +101 -0
  117. package/rispecs/04-narrative-engine.spec.md +185 -0
  118. package/rispecs/05-pde-engine.spec.md +112 -0
  119. package/rispecs/06-planning-engine.spec.md +128 -0
  120. package/rispecs/07-pipeline-templates.spec.md +97 -0
  121. package/rispecs/08-cli-interface.spec.md +134 -0
  122. package/rispecs/09-mcp-server.spec.md +140 -0
  123. package/rispecs/10-audio-module.spec.md +63 -0
  124. package/rispecs/KINSHIP.md +56 -0
  125. package/rispecs/README.md +100 -0
  126. package/src/audio.ts +76 -0
  127. package/src/cli-helpers.ts +86 -0
  128. package/src/cli.ts +1223 -0
  129. package/src/config.ts +172 -0
  130. package/src/environment.ts +171 -0
  131. package/src/github.ts +143 -0
  132. package/src/langfuse/client.ts +105 -0
  133. package/src/langfuse/comments.ts +52 -0
  134. package/src/langfuse/datasets.ts +178 -0
  135. package/src/langfuse/index.ts +33 -0
  136. package/src/langfuse/media.ts +193 -0
  137. package/src/langfuse/observations.ts +131 -0
  138. package/src/langfuse/prompts.ts +157 -0
  139. package/src/langfuse/scores.ts +456 -0
  140. package/src/langfuse/traces.ts +276 -0
  141. package/src/llm.ts +106 -0
  142. package/src/narrative/graph-manager.ts +1358 -0
  143. package/src/narrative/index.ts +32 -0
  144. package/src/narrative/markdown-export.ts +535 -0
  145. package/src/narrative/tool-definitions.ts +635 -0
  146. package/src/narrative/tool-handlers.ts +528 -0
  147. package/src/narrative/types.ts +9 -0
  148. package/src/narrative/validation.ts +179 -0
  149. package/src/pde/index.ts +8 -0
  150. package/src/pde/mcp-handlers.ts +359 -0
  151. package/src/pde/mcp-tools.ts +201 -0
  152. package/src/pde/session-manager.ts +248 -0
  153. package/src/pde/stc-mapper.ts +298 -0
  154. package/src/pipeline/index.ts +7 -0
  155. package/src/pipeline/template-engine.ts +398 -0
  156. package/src/planning/index.ts +13 -0
  157. package/src/planning/mcp-handlers.ts +369 -0
  158. package/src/planning/mcp-tools.ts +155 -0
  159. package/src/planning/plan-parser.ts +587 -0
  160. package/src/redis.ts +97 -0
  161. package/src/types.ts +280 -0
  162. package/tsconfig.json +26 -0
@@ -0,0 +1,366 @@
1
+ // coaiajs/src/langfuse/scores.ts — Score operations
2
+ // Port of cofuse.py score functions including configs, presets, import/export
3
+ import { readFileSync, writeFileSync } from 'node:fs';
4
+ import { getClient, nowISO } from './client.js';
5
+ // ─── Score CRUD ─────────────────────────────────────────────────────
6
+ export async function createScore(params) {
7
+ const client = getClient();
8
+ const data = { value: params.value };
9
+ if (params.id)
10
+ data.id = params.id;
11
+ if (params.name)
12
+ data.name = params.name;
13
+ if (params.traceId)
14
+ data.traceId = params.traceId;
15
+ if (params.observationId)
16
+ data.observationId = params.observationId;
17
+ if (params.configId)
18
+ data.configId = params.configId;
19
+ if (params.comment)
20
+ data.comment = params.comment;
21
+ const result = await client.request('POST', '/api/public/scores', data);
22
+ return JSON.stringify(result, null, 2);
23
+ }
24
+ export async function applyScoreToTrace(traceId, scoreId, value) {
25
+ return createScoreForTarget({
26
+ targetType: 'trace',
27
+ targetId: traceId,
28
+ scoreId,
29
+ value,
30
+ });
31
+ }
32
+ export async function createScoreForTarget(params) {
33
+ const client = getClient();
34
+ const data = { value: params.value };
35
+ if (params.targetType === 'trace') {
36
+ data.traceId = params.targetId;
37
+ if (params.observationId)
38
+ data.observationId = params.observationId;
39
+ }
40
+ else {
41
+ data.sessionId = params.targetId;
42
+ }
43
+ if (params.configId) {
44
+ data.configId = params.configId;
45
+ }
46
+ else {
47
+ if (params.scoreId)
48
+ data.id = params.scoreId;
49
+ if (params.scoreName)
50
+ data.name = params.scoreName;
51
+ }
52
+ if (params.comment)
53
+ data.comment = params.comment;
54
+ const result = await client.request('POST', '/api/public/scores', data);
55
+ return JSON.stringify(result, null, 2);
56
+ }
57
+ export async function listScores(filters) {
58
+ const client = getClient();
59
+ const allScores = [];
60
+ let page = filters.page ?? 1;
61
+ while (true) {
62
+ const params = new URLSearchParams({ page: String(page) });
63
+ if (filters.userId)
64
+ params.set('userId', filters.userId);
65
+ if (filters.name)
66
+ params.set('name', filters.name);
67
+ if (filters.fromTimestamp)
68
+ params.set('fromTimestamp', filters.fromTimestamp);
69
+ if (filters.toTimestamp)
70
+ params.set('toTimestamp', filters.toTimestamp);
71
+ if (filters.configId)
72
+ params.set('configId', filters.configId);
73
+ const data = await client.request('GET', `/api/public/v2/scores?${params.toString()}`);
74
+ const scores = data.data;
75
+ if (!scores || !Array.isArray(scores) || scores.length === 0)
76
+ break;
77
+ allScores.push(...scores);
78
+ if (!shouldContinuePagination(data, page))
79
+ break;
80
+ page++;
81
+ }
82
+ return JSON.stringify(allScores, null, 2);
83
+ }
84
+ // ─── Score Configs ──────────────────────────────────────────────────
85
+ export async function listScoreConfigs() {
86
+ const client = getClient();
87
+ const allConfigs = [];
88
+ let page = 1;
89
+ while (true) {
90
+ const data = await client.request('GET', `/api/public/score-configs?page=${page}`);
91
+ const configs = data.data;
92
+ if (!configs || !Array.isArray(configs) || configs.length === 0)
93
+ break;
94
+ allConfigs.push(...configs);
95
+ if (!shouldContinuePagination(data, page))
96
+ break;
97
+ page++;
98
+ }
99
+ return JSON.stringify(allConfigs, null, 2);
100
+ }
101
+ export async function getScoreConfig(configId) {
102
+ const client = getClient();
103
+ const result = await client.request('GET', `/api/public/score-configs/${configId}`);
104
+ return JSON.stringify(result, null, 2);
105
+ }
106
+ export async function createScoreConfig(params) {
107
+ const client = getClient();
108
+ const data = {
109
+ name: params.name,
110
+ dataType: params.dataType,
111
+ };
112
+ if (params.description)
113
+ data.description = params.description;
114
+ if (params.categories)
115
+ data.categories = params.categories;
116
+ if (params.minValue !== undefined)
117
+ data.minValue = params.minValue;
118
+ if (params.maxValue !== undefined)
119
+ data.maxValue = params.maxValue;
120
+ const result = await client.request('POST', '/api/public/score-configs', data);
121
+ return JSON.stringify(result, null, 2);
122
+ }
123
+ export async function exportScoreConfigs(outputFile) {
124
+ const configsRaw = await listScoreConfigs();
125
+ const configs = JSON.parse(configsRaw);
126
+ const exported = configs.map((c) => ({
127
+ name: c.name,
128
+ dataType: c.dataType,
129
+ description: c.description,
130
+ categories: c.categories,
131
+ minValue: c.minValue,
132
+ maxValue: c.maxValue,
133
+ metadata: {
134
+ id: c.id,
135
+ createdAt: c.createdAt,
136
+ updatedAt: c.updatedAt,
137
+ projectId: c.projectId,
138
+ isArchived: c.isArchived,
139
+ },
140
+ }));
141
+ const exportData = {
142
+ version: '1.0',
143
+ exportedAt: nowISO(),
144
+ totalConfigs: exported.length,
145
+ configs: exported,
146
+ };
147
+ const json = JSON.stringify(exportData, null, 2);
148
+ if (outputFile) {
149
+ writeFileSync(outputFile, json, 'utf-8');
150
+ }
151
+ return json;
152
+ }
153
+ export async function importScoreConfigs(importFile, options) {
154
+ let importData;
155
+ try {
156
+ importData = JSON.parse(readFileSync(importFile, 'utf-8'));
157
+ }
158
+ catch (e) {
159
+ return `❌ Failed to read import file: ${e}`;
160
+ }
161
+ const configsList = Array.isArray(importData)
162
+ ? importData
163
+ : importData.configs ?? [];
164
+ if (!configsList.length)
165
+ return '❌ No score configs found in import file.';
166
+ const existingRaw = await listScoreConfigs();
167
+ const existing = JSON.parse(existingRaw);
168
+ const existingNames = new Set(existing.map((c) => String(c.name).toLowerCase()));
169
+ const selected = options?.selectedConfigs
170
+ ? new Set(options.selectedConfigs.map((n) => n.toLowerCase()))
171
+ : null;
172
+ const results = [];
173
+ let imported = 0;
174
+ let skipped = 0;
175
+ for (const raw of configsList) {
176
+ const cfg = raw;
177
+ const name = String(cfg.name);
178
+ if (selected && !selected.has(name.toLowerCase()))
179
+ continue;
180
+ if (existingNames.has(name.toLowerCase()) && !options?.allowDuplicates) {
181
+ results.push(` ⏭️ '${name}' — skipped (duplicate)`);
182
+ skipped++;
183
+ continue;
184
+ }
185
+ try {
186
+ const result = await createScoreConfig({
187
+ name,
188
+ dataType: cfg.dataType,
189
+ description: cfg.description,
190
+ categories: cfg.categories,
191
+ minValue: cfg.minValue,
192
+ maxValue: cfg.maxValue,
193
+ });
194
+ const parsed = JSON.parse(result);
195
+ results.push(` ✅ '${name}' (ID: ${parsed.id ?? 'created'})`);
196
+ imported++;
197
+ }
198
+ catch (e) {
199
+ results.push(` ❌ '${name}' — ${e}`);
200
+ }
201
+ }
202
+ return [
203
+ `📁 Import: ${importFile}`,
204
+ `📊 Summary: ${imported} imported, ${skipped} skipped`,
205
+ ...results,
206
+ ].join('\n');
207
+ }
208
+ // ─── Score Config Caching / Apply ───────────────────────────────────
209
+ export async function applyScoreConfig(configNameOrId, targetType, targetId, value, observationId, comment) {
210
+ // Resolve config: try by ID first, then search by name
211
+ let configId;
212
+ try {
213
+ const configJson = await getScoreConfig(configNameOrId);
214
+ const config = JSON.parse(configJson);
215
+ if (config.id)
216
+ configId = config.id;
217
+ }
218
+ catch {
219
+ // Try listing and searching by name
220
+ const allJson = await listScoreConfigs();
221
+ const all = JSON.parse(allJson);
222
+ const match = all.find((c) => String(c.name).toLowerCase() === configNameOrId.toLowerCase());
223
+ if (match)
224
+ configId = String(match.id);
225
+ }
226
+ if (!configId) {
227
+ return JSON.stringify({ error: `Score config '${configNameOrId}' not found` });
228
+ }
229
+ return createScoreForTarget({
230
+ targetType: targetType,
231
+ targetId,
232
+ configId,
233
+ value,
234
+ observationId,
235
+ comment,
236
+ });
237
+ }
238
+ // ─── Presets ────────────────────────────────────────────────────────
239
+ export function getBuiltInPresets() {
240
+ return BUILT_IN_PRESETS.map((p) => ({ ...p }));
241
+ }
242
+ export async function installPreset(name, checkDuplicates = true) {
243
+ const preset = BUILT_IN_PRESETS.find((p) => p.name.toLowerCase() === name.toLowerCase());
244
+ if (!preset) {
245
+ return JSON.stringify({ error: `Preset '${name}' not found` });
246
+ }
247
+ if (checkDuplicates) {
248
+ const existingRaw = await listScoreConfigs();
249
+ const existing = JSON.parse(existingRaw);
250
+ const dup = existing.find((c) => String(c.name).toLowerCase() === preset.name.toLowerCase());
251
+ if (dup) {
252
+ return JSON.stringify({
253
+ warning: `Score config '${preset.name}' already exists (ID: ${dup.id})`,
254
+ existingId: dup.id,
255
+ });
256
+ }
257
+ }
258
+ return createScoreConfig({
259
+ name: preset.name,
260
+ dataType: preset.dataType,
261
+ description: preset.description,
262
+ categories: preset.categories,
263
+ minValue: preset.minValue,
264
+ maxValue: preset.maxValue,
265
+ });
266
+ }
267
+ // ─── Formatters ─────────────────────────────────────────────────────
268
+ export function formatScoresTable(json) {
269
+ try {
270
+ const raw = typeof json === 'string' ? JSON.parse(json) : json;
271
+ const scores = Array.isArray(raw)
272
+ ? raw
273
+ : raw.data ?? [];
274
+ if (!scores.length)
275
+ return 'No scores found.';
276
+ const headers = ['ID', 'Name', 'Value', 'Created', 'Trace ID'];
277
+ const rows = scores.map((s) => [
278
+ trunc(String(s.id ?? 'N/A'), 20),
279
+ trunc(String(s.name ?? 'N/A'), 20),
280
+ String(s.value ?? 'N/A'),
281
+ String(s.createdAt ?? 'N/A').slice(0, 16),
282
+ trunc(String(s.traceId ?? 'N/A'), 20),
283
+ ]);
284
+ return formatTable(headers, rows) + `\nTotal scores: ${scores.length}`;
285
+ }
286
+ catch (e) {
287
+ return `Error formatting scores: ${e}`;
288
+ }
289
+ }
290
+ export function formatScoreConfigsTable(json) {
291
+ try {
292
+ const raw = typeof json === 'string' ? JSON.parse(json) : json;
293
+ const configs = Array.isArray(raw)
294
+ ? raw
295
+ : raw.data ?? [];
296
+ if (!configs.length)
297
+ return 'No score configs found.';
298
+ const headers = ['ID', 'Name', 'Data Type', 'Description', 'Created'];
299
+ const rows = configs.map((c) => [
300
+ trunc(String(c.id ?? 'N/A'), 20),
301
+ trunc(String(c.name ?? 'N/A'), 20),
302
+ String(c.dataType ?? 'N/A'),
303
+ trunc(String(c.description ?? 'N/A'), 35),
304
+ String(c.createdAt ?? 'N/A').slice(0, 16),
305
+ ]);
306
+ return formatTable(headers, rows) + `\nTotal score configs: ${configs.length}`;
307
+ }
308
+ catch (e) {
309
+ return `Error formatting score configs: ${e}`;
310
+ }
311
+ }
312
+ // ─── Built-in Presets ───────────────────────────────────────────────
313
+ const BUILT_IN_PRESETS = [
314
+ // Narrative & Storytelling
315
+ { name: 'Narrative Coherence', dataType: 'CATEGORICAL', description: 'Evaluates how well story elements connect and flow together logically.', categories: [{ label: 'Incoherent', value: 1 }, { label: 'Loosely Connected', value: 2 }, { label: 'Coherent', value: 3 }, { label: 'Well-Structured', value: 4 }, { label: 'Masterfully Woven', value: 5 }] },
316
+ { name: 'Character Development', dataType: 'CATEGORICAL', description: 'Measures the depth and growth of characters throughout the narrative.', categories: [{ label: 'Flat/Static', value: 1 }, { label: 'Basic Development', value: 2 }, { label: 'Moderate Growth', value: 3 }, { label: 'Rich Development', value: 4 }, { label: 'Complex Evolution', value: 5 }] },
317
+ { name: 'Emotional Resonance', dataType: 'CATEGORICAL', description: 'Evaluates the emotional impact and connection with the audience.', categories: [{ label: 'No Emotional Impact', value: 1 }, { label: 'Mild Resonance', value: 2 }, { label: 'Moderate Impact', value: 3 }, { label: 'Strong Emotional Connection', value: 4 }, { label: 'Deeply Moving', value: 5 }] },
318
+ { name: 'Originality', dataType: 'CATEGORICAL', description: 'Evaluates the uniqueness and freshness of the ideas or expression.', categories: [{ label: 'Unoriginal', value: 1 }, { label: 'Low Originality', value: 2 }, { label: 'Moderate Originality', value: 3 }, { label: 'High Originality', value: 4 }, { label: 'Highly Original', value: 5 }] },
319
+ { name: 'Thematic Depth', dataType: 'CATEGORICAL', description: 'Measures the depth and sophistication of underlying themes.', categories: [{ label: 'Surface Level', value: 1 }, { label: 'Basic Themes', value: 2 }, { label: 'Developed Themes', value: 3 }, { label: 'Rich Thematic Content', value: 4 }, { label: 'Profound Depth', value: 5 }] },
320
+ // AI Response Evaluation
321
+ { name: 'Helpfulness', dataType: 'CATEGORICAL', description: 'Measures how well the response addresses the user\'s needs.', categories: [{ label: 'Not Helpful', value: 1 }, { label: 'Slightly Helpful', value: 2 }, { label: 'Moderately Helpful', value: 3 }, { label: 'Very Helpful', value: 4 }, { label: 'Extremely Helpful', value: 5 }] },
322
+ { name: 'Accuracy', dataType: 'CATEGORICAL', description: 'Evaluates the factual correctness and precision of information.', categories: [{ label: 'Inaccurate', value: 1 }, { label: 'Mostly Inaccurate', value: 2 }, { label: 'Partially Accurate', value: 3 }, { label: 'Mostly Accurate', value: 4 }, { label: 'Completely Accurate', value: 5 }] },
323
+ { name: 'Safety', dataType: 'CATEGORICAL', description: 'Assesses whether the content is safe and free from harmful elements.', categories: [{ label: 'Unsafe/Harmful', value: 1 }, { label: 'Potentially Unsafe', value: 2 }, { label: 'Neutral/Safe', value: 3 }, { label: 'Very Safe', value: 4 }, { label: 'Exemplarily Safe', value: 5 }] },
324
+ { name: 'Relevance', dataType: 'CATEGORICAL', description: 'Measures how well the response relates to the specific query.', categories: [{ label: 'Irrelevant', value: 1 }, { label: 'Slightly Relevant', value: 2 }, { label: 'Moderately Relevant', value: 3 }, { label: 'Highly Relevant', value: 4 }, { label: 'Perfectly Relevant', value: 5 }] },
325
+ { name: 'Completeness', dataType: 'CATEGORICAL', description: 'Evaluates whether the response fully addresses all aspects.', categories: [{ label: 'Incomplete', value: 1 }, { label: 'Partially Complete', value: 2 }, { label: 'Mostly Complete', value: 3 }, { label: 'Very Complete', value: 4 }, { label: 'Comprehensive', value: 5 }] },
326
+ // General Content
327
+ { name: 'Clarity', dataType: 'CATEGORICAL', description: 'Measures how easy it is to understand the content.', categories: [{ label: 'Unclear', value: 1 }, { label: 'Moderately Clear', value: 2 }, { label: 'Clear', value: 3 }, { label: 'Very Clear', value: 4 }, { label: 'Excellent Clarity', value: 5 }] },
328
+ { name: 'Engagement', dataType: 'CATEGORICAL', description: 'Evaluates how well the content captures attention.', categories: [{ label: 'Disengaging', value: 1 }, { label: 'Slightly Engaging', value: 2 }, { label: 'Engaging', value: 3 }, { label: 'Very Engaging', value: 4 }, { label: 'Highly Engaging', value: 5 }] },
329
+ { name: 'Tone Appropriateness', dataType: 'CATEGORICAL', description: 'Assesses whether the tone matches context and audience expectations.', categories: [{ label: 'Inappropriate Tone', value: 1 }, { label: 'Somewhat Inappropriate', value: 2 }, { label: 'Acceptable Tone', value: 3 }, { label: 'Well-Matched Tone', value: 4 }, { label: 'Perfect Tone', value: 5 }] },
330
+ { name: 'Conciseness', dataType: 'CATEGORICAL', description: 'Evaluates the efficiency of communication.', categories: [{ label: 'Verbose/Wordy', value: 1 }, { label: 'Somewhat Verbose', value: 2 }, { label: 'Balanced', value: 3 }, { label: 'Concise', value: 4 }, { label: 'Perfectly Concise', value: 5 }] },
331
+ // Technical Quality
332
+ { name: 'Structure & Organization', dataType: 'CATEGORICAL', description: 'Evaluates the logical organization and structural quality.', categories: [{ label: 'Disorganized', value: 1 }, { label: 'Basic Structure', value: 2 }, { label: 'Well-Organized', value: 3 }, { label: 'Excellent Structure', value: 4 }, { label: 'Masterful Organization', value: 5 }] },
333
+ { name: 'Language Quality', dataType: 'CATEGORICAL', description: 'Assesses grammar, vocabulary usage, and linguistic competence.', categories: [{ label: 'Poor Language', value: 1 }, { label: 'Fair Language', value: 2 }, { label: 'Good Language', value: 3 }, { label: 'Excellent Language', value: 4 }, { label: 'Exceptional Language', value: 5 }] },
334
+ // Specialized
335
+ { name: 'Critical Thinking', dataType: 'CATEGORICAL', description: 'Measures the depth of analysis, reasoning, and intellectual rigor.', categories: [{ label: 'No Critical Analysis', value: 1 }, { label: 'Basic Analysis', value: 2 }, { label: 'Sound Reasoning', value: 3 }, { label: 'Strong Critical Thinking', value: 4 }, { label: 'Exceptional Analysis', value: 5 }] },
336
+ { name: 'Evidence Support', dataType: 'CATEGORICAL', description: 'Evaluates the use and quality of supporting evidence.', categories: [{ label: 'No Evidence', value: 1 }, { label: 'Weak Evidence', value: 2 }, { label: 'Adequate Evidence', value: 3 }, { label: 'Strong Evidence', value: 4 }, { label: 'Compelling Evidence', value: 5 }] },
337
+ { name: 'Innovation', dataType: 'CATEGORICAL', description: 'Assesses creativity and novel approaches in problem-solving.', categories: [{ label: 'Conventional', value: 1 }, { label: 'Slightly Creative', value: 2 }, { label: 'Moderately Innovative', value: 3 }, { label: 'Highly Innovative', value: 4 }, { label: 'Groundbreaking', value: 5 }] },
338
+ // Numeric
339
+ { name: 'Overall Quality', dataType: 'NUMERIC', description: 'General numeric assessment of overall content quality.', minValue: 0, maxValue: 10 },
340
+ { name: 'Performance Score', dataType: 'NUMERIC', description: 'Numeric performance evaluation score.', minValue: 0, maxValue: 100 },
341
+ // Boolean
342
+ { name: 'Meets Requirements', dataType: 'BOOLEAN', description: 'Binary assessment of whether content meets specified requirements.' },
343
+ ];
344
+ // ─── Helpers ────────────────────────────────────────────────────────
345
+ function shouldContinuePagination(data, currentPage) {
346
+ const meta = data.meta;
347
+ if (meta?.totalPages) {
348
+ return (meta.page ?? currentPage) < meta.totalPages;
349
+ }
350
+ if (data.hasNextPage)
351
+ return true;
352
+ if (data.totalPages && currentPage < data.totalPages)
353
+ return true;
354
+ return false;
355
+ }
356
+ function trunc(s, len) {
357
+ return s.length > len ? s.slice(0, len - 3) + '...' : s;
358
+ }
359
+ function formatTable(headers, rows) {
360
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)));
361
+ const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
362
+ const hdr = '| ' + headers.map((h, i) => h.padEnd(widths[i])).join(' | ') + ' |';
363
+ const body = rows.map((r) => '| ' + r.map((c, i) => (c ?? '').padEnd(widths[i])).join(' | ') + ' |');
364
+ return [sep, hdr, sep, ...body, sep].join('\n');
365
+ }
366
+ //# sourceMappingURL=scores.js.map
@@ -0,0 +1,29 @@
1
+ export interface TraceFilters {
2
+ sessionId?: string;
3
+ userId?: string;
4
+ name?: string;
5
+ tags?: string[];
6
+ fromTimestamp?: string;
7
+ toTimestamp?: string;
8
+ orderBy?: string;
9
+ version?: string;
10
+ release?: string;
11
+ environment?: string[];
12
+ page?: number;
13
+ limit?: number;
14
+ }
15
+ export declare function addTrace(params: {
16
+ traceId?: string;
17
+ userId?: string;
18
+ sessionId?: string;
19
+ name?: string;
20
+ inputData?: unknown;
21
+ outputData?: unknown;
22
+ metadata?: unknown;
23
+ }): Promise<string>;
24
+ export declare function patchTraceOutput(traceId: string, outputData: unknown): Promise<string>;
25
+ export declare function listTraces(filters: TraceFilters): Promise<string>;
26
+ export declare function getTrace(traceId: string): Promise<string>;
27
+ export declare function formatTracesTable(json: unknown): string;
28
+ export declare function formatTraceTree(json: unknown): string;
29
+ //# sourceMappingURL=traces.d.ts.map
@@ -0,0 +1,225 @@
1
+ // coaiajs/src/langfuse/traces.ts — Trace operations
2
+ // Port of cofuse.py trace functions
3
+ import { v4 as uuidv4 } from 'uuid';
4
+ import { getClient, nowISO } from './client.js';
5
+ export async function addTrace(params) {
6
+ const client = getClient();
7
+ const now = nowISO();
8
+ const traceId = params.traceId ?? uuidv4();
9
+ const body = {
10
+ id: traceId,
11
+ timestamp: now,
12
+ };
13
+ if (params.sessionId)
14
+ body.sessionId = params.sessionId;
15
+ if (params.name)
16
+ body.name = params.name;
17
+ if (params.inputData)
18
+ body.input = params.inputData;
19
+ if (params.outputData)
20
+ body.output = params.outputData;
21
+ if (params.userId)
22
+ body.userId = params.userId;
23
+ if (params.metadata)
24
+ body.metadata = params.metadata;
25
+ const event = {
26
+ id: `${traceId}-event`,
27
+ timestamp: now,
28
+ type: 'trace-create',
29
+ body,
30
+ };
31
+ await client.ingest([event]);
32
+ return JSON.stringify({
33
+ success: true,
34
+ traceId,
35
+ message: `Trace ${traceId} created`,
36
+ }, null, 2);
37
+ }
38
+ export async function patchTraceOutput(traceId, outputData) {
39
+ const client = getClient();
40
+ const now = nowISO();
41
+ const body = {
42
+ id: traceId,
43
+ timestamp: now,
44
+ output: outputData,
45
+ };
46
+ const eventId = `${traceId}-patch-${uuidv4().slice(0, 8)}`;
47
+ const event = {
48
+ id: eventId,
49
+ timestamp: now,
50
+ type: 'trace-create',
51
+ body,
52
+ };
53
+ await client.ingest([event]);
54
+ return JSON.stringify({
55
+ success: true,
56
+ traceId,
57
+ message: `Trace output patched for ${traceId}`,
58
+ }, null, 2);
59
+ }
60
+ export async function listTraces(filters) {
61
+ const client = getClient();
62
+ const params = new URLSearchParams();
63
+ if (filters.sessionId)
64
+ params.set('sessionId', filters.sessionId);
65
+ if (filters.userId)
66
+ params.set('userId', filters.userId);
67
+ if (filters.name)
68
+ params.set('name', filters.name);
69
+ if (filters.fromTimestamp)
70
+ params.set('fromTimestamp', filters.fromTimestamp);
71
+ if (filters.toTimestamp)
72
+ params.set('toTimestamp', filters.toTimestamp);
73
+ if (filters.orderBy)
74
+ params.set('orderBy', filters.orderBy);
75
+ if (filters.version)
76
+ params.set('version', filters.version);
77
+ if (filters.release)
78
+ params.set('release', filters.release);
79
+ if (filters.tags) {
80
+ for (const tag of filters.tags)
81
+ params.append('tags', tag);
82
+ }
83
+ if (filters.environment) {
84
+ for (const env of filters.environment)
85
+ params.append('environment', env);
86
+ }
87
+ const page = filters.page ?? 1;
88
+ const limit = filters.limit ?? 50;
89
+ params.set('page', String(page));
90
+ params.set('limit', String(limit));
91
+ const qs = params.toString();
92
+ const path = `/api/public/traces${qs ? `?${qs}` : ''}`;
93
+ const result = await client.request('GET', path);
94
+ return JSON.stringify(result, null, 2);
95
+ }
96
+ export async function getTrace(traceId) {
97
+ const client = getClient();
98
+ const trace = await client.request('GET', `/api/public/traces/${traceId}`);
99
+ // Fetch observations for this trace
100
+ try {
101
+ const obsResult = await client.request('GET', `/api/public/observations?traceId=${traceId}`);
102
+ if (obsResult && typeof obsResult === 'object' && 'data' in obsResult) {
103
+ trace.observations = obsResult.data;
104
+ }
105
+ else {
106
+ trace.observations = obsResult;
107
+ }
108
+ }
109
+ catch {
110
+ trace.observations = [];
111
+ }
112
+ return JSON.stringify(trace, null, 2);
113
+ }
114
+ // ─── Formatters ─────────────────────────────────────────────────────
115
+ export function formatTracesTable(json) {
116
+ try {
117
+ const data = typeof json === 'string' ? JSON.parse(json) : json;
118
+ const traces = Array.isArray(data)
119
+ ? data
120
+ : data.data ?? [];
121
+ if (!traces.length)
122
+ return 'No traces found.';
123
+ const headers = ['ID', 'Name', 'Session', 'User', 'Time'];
124
+ const rows = traces.map((t) => [
125
+ trunc(String(t.id ?? 'N/A'), 20),
126
+ trunc(String(t.name ?? 'Unnamed'), 20),
127
+ trunc(String(t.sessionId ?? 'N/A'), 15),
128
+ trunc(String(t.userId ?? 'N/A'), 12),
129
+ trunc(String(t.timestamp ?? 'N/A').slice(0, 19), 19),
130
+ ]);
131
+ return formatTable(headers, rows) + `\nTotal traces: ${traces.length}`;
132
+ }
133
+ catch (e) {
134
+ return `Error formatting traces: ${e}`;
135
+ }
136
+ }
137
+ const TYPE_GLYPHS = {
138
+ SPAN: '🔗',
139
+ GENERATION: '🤖',
140
+ EVENT: '⚡',
141
+ SCORE: '📊',
142
+ TRACE: '🛤️',
143
+ DEFAULT: '📦',
144
+ };
145
+ export function formatTraceTree(json) {
146
+ try {
147
+ const trace = (typeof json === 'string' ? JSON.parse(json) : json);
148
+ if ('error' in trace)
149
+ return `Error: ${trace.error}`;
150
+ const BRANCH = '├── ';
151
+ const LAST = '└── ';
152
+ const VERT = '│ ';
153
+ const SPACE = ' ';
154
+ const lines = [];
155
+ const name = String(trace.name ?? 'Unnamed');
156
+ const id = String(trace.id ?? 'N/A');
157
+ const userId = String(trace.userId ?? 'N/A');
158
+ const sessionId = String(trace.sessionId ?? 'N/A');
159
+ const ts = String(trace.timestamp ?? 'N/A').slice(0, 19);
160
+ lines.push(`🔗 Trace: ${name}`);
161
+ lines.push(`${BRANCH}🆔 ID: ${id}`);
162
+ lines.push(`${BRANCH}👤 User: ${userId}`);
163
+ lines.push(`${BRANCH}🔗 Session: ${sessionId}`);
164
+ lines.push(`${BRANCH}⏰ Time: ${ts}`);
165
+ const metadata = trace.metadata;
166
+ if (metadata && typeof metadata === 'object' && Object.keys(metadata).length) {
167
+ lines.push(`${BRANCH}📋 Metadata:`);
168
+ const entries = Object.entries(metadata);
169
+ entries.forEach(([k, v], i) => {
170
+ const pre = i === entries.length - 1 ? LAST : BRANCH;
171
+ lines.push(`${VERT}${pre}${k}: ${v}`);
172
+ });
173
+ }
174
+ const observations = (trace.observations ?? []);
175
+ if (!observations.length) {
176
+ lines.push(`${LAST}📝 No observations`);
177
+ return lines.join('\n');
178
+ }
179
+ lines.push(`${LAST}📝 Observations (${observations.length}):`);
180
+ const roots = observations.filter((o) => !o.parentObservationId);
181
+ function addTree(list, prefix) {
182
+ list.forEach((obs, i) => {
183
+ const isLast = i === list.length - 1;
184
+ const obsName = String(obs.name ?? `Obs ${String(obs.id ?? '').slice(0, 8)}`);
185
+ const obsType = String(obs.type ?? 'UNKNOWN').toUpperCase();
186
+ const obsTime = String(obs.startTime ?? 'N/A').slice(0, 19);
187
+ const glyph = TYPE_GLYPHS[obsType] ?? TYPE_GLYPHS.DEFAULT;
188
+ const sym = isLast ? LAST : BRANCH;
189
+ const np = prefix + (isLast ? SPACE : VERT);
190
+ lines.push(`${prefix}${sym}${glyph} [${obsType}] ${obsName} (${obs.id})`);
191
+ lines.push(`${np}${BRANCH}⏰ ${obsTime}`);
192
+ if (obs.input) {
193
+ const txt = trunc(String(obs.input).replace(/\n/g, ' '), 90);
194
+ lines.push(`${np}${BRANCH}📥 Input: ${txt}`);
195
+ }
196
+ if (obs.output) {
197
+ const txt = trunc(String(obs.output).replace(/\n/g, ' '), 90);
198
+ lines.push(`${np}${LAST}📤 Output: ${txt}`);
199
+ }
200
+ const children = observations.filter((c) => c.parentObservationId === obs.id);
201
+ if (children.length) {
202
+ lines.push(`${np}${LAST}🌿 Children (${children.length}):`);
203
+ addTree(children, np + SPACE);
204
+ }
205
+ });
206
+ }
207
+ addTree(roots, SPACE);
208
+ return lines.join('\n');
209
+ }
210
+ catch (e) {
211
+ return `Error formatting trace tree: ${e}`;
212
+ }
213
+ }
214
+ // ─── Helpers ────────────────────────────────────────────────────────
215
+ function trunc(s, len) {
216
+ return s.length > len ? s.slice(0, len - 3) + '...' : s;
217
+ }
218
+ function formatTable(headers, rows) {
219
+ const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)));
220
+ const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
221
+ const hdr = '| ' + headers.map((h, i) => h.padEnd(widths[i])).join(' | ') + ' |';
222
+ const body = rows.map((r) => '| ' + r.map((c, i) => (c ?? '').padEnd(widths[i])).join(' | ') + ' |');
223
+ return [sep, hdr, sep, ...body, sep].join('\n');
224
+ }
225
+ //# sourceMappingURL=traces.js.map
@@ -0,0 +1,23 @@
1
+ /** Reset client (testing). */
2
+ export declare function resetClient(): void;
3
+ /**
4
+ * Send a chat completion request.
5
+ * Minimal wrapper matching coaiapy's `llm()` function.
6
+ */
7
+ export declare function llm(user: string, system?: string, temperature?: number): Promise<string>;
8
+ /**
9
+ * Transcribe audio using Whisper API.
10
+ * Parity with coaiapy's transcription functions.
11
+ */
12
+ export declare function transcribeAudio(filePath: string): Promise<string>;
13
+ /**
14
+ * Generate an image using DALL-E.
15
+ * Returns the URL of the generated image.
16
+ */
17
+ export declare function generateImage(prompt: string, size?: string): Promise<string>;
18
+ /**
19
+ * Abstract process: config-driven LLM call with a named process.
20
+ * Looks up system prompt by processName in config or uses a default.
21
+ */
22
+ export declare function abstractProcess(processName: string, input: string): Promise<string>;
23
+ //# sourceMappingURL=llm.d.ts.map