coaiajs 0.3.0 → 0.3.2

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 (179) hide show
  1. package/KINSHIP.md +1 -1
  2. package/README.md +191 -75
  3. package/dist/mcp/config.d.ts.map +1 -0
  4. package/dist/mcp/config.js +0 -2
  5. package/dist/mcp/config.js.map +1 -0
  6. package/dist/mcp/prompts.d.ts.map +1 -0
  7. package/dist/mcp/prompts.js.map +1 -0
  8. package/dist/mcp/resources.d.ts.map +1 -0
  9. package/dist/mcp/resources.js.map +1 -0
  10. package/dist/mcp/server.d.ts.map +1 -0
  11. package/dist/mcp/server.js +1 -1
  12. package/dist/mcp/server.js.map +1 -0
  13. package/dist/mcp/tools/coaiapy-tools.d.ts.map +1 -0
  14. package/dist/mcp/tools/coaiapy-tools.js.map +1 -0
  15. package/dist/mcp/tools/index.d.ts.map +1 -0
  16. package/dist/mcp/tools/index.js.map +1 -0
  17. package/dist/src/audio.d.ts.map +1 -0
  18. package/dist/src/audio.js.map +1 -0
  19. package/dist/src/cli-helpers.d.ts.map +1 -0
  20. package/dist/src/cli-helpers.js.map +1 -0
  21. package/dist/src/cli.d.ts.map +1 -0
  22. package/dist/src/cli.js +1 -0
  23. package/dist/src/cli.js.map +1 -0
  24. package/dist/src/config.d.ts.map +1 -0
  25. package/dist/src/config.js.map +1 -0
  26. package/dist/src/environment.d.ts.map +1 -0
  27. package/dist/src/environment.js.map +1 -0
  28. package/dist/src/github.d.ts.map +1 -0
  29. package/dist/src/github.js.map +1 -0
  30. package/dist/src/index.d.ts +1 -0
  31. package/dist/src/index.d.ts.map +1 -0
  32. package/dist/src/index.js +1 -0
  33. package/dist/src/index.js.map +1 -0
  34. package/dist/src/langfuse/client.d.ts.map +1 -0
  35. package/dist/src/langfuse/client.js.map +1 -0
  36. package/dist/src/langfuse/comments.d.ts.map +1 -0
  37. package/dist/src/langfuse/comments.js.map +1 -0
  38. package/dist/src/langfuse/datasets.d.ts.map +1 -0
  39. package/dist/src/langfuse/datasets.js.map +1 -0
  40. package/dist/src/langfuse/index.d.ts +1 -0
  41. package/dist/src/langfuse/index.d.ts.map +1 -0
  42. package/dist/src/langfuse/index.js.map +1 -0
  43. package/dist/src/langfuse/media.d.ts.map +1 -0
  44. package/dist/src/langfuse/media.js.map +1 -0
  45. package/dist/src/langfuse/observations.d.ts.map +1 -0
  46. package/dist/src/langfuse/observations.js.map +1 -0
  47. package/dist/src/langfuse/prompts.d.ts +5 -1
  48. package/dist/src/langfuse/prompts.d.ts.map +1 -0
  49. package/dist/src/langfuse/prompts.js +16 -3
  50. package/dist/src/langfuse/prompts.js.map +1 -0
  51. package/dist/src/langfuse/scores.d.ts.map +1 -0
  52. package/dist/src/langfuse/scores.js.map +1 -0
  53. package/dist/src/langfuse/traces.d.ts.map +1 -0
  54. package/dist/src/langfuse/traces.js.map +1 -0
  55. package/dist/src/llm.d.ts.map +1 -0
  56. package/dist/src/llm.js.map +1 -0
  57. package/dist/src/narrative/graph-manager.d.ts.map +1 -0
  58. package/dist/src/narrative/graph-manager.js.map +1 -0
  59. package/dist/src/narrative/index.d.ts.map +1 -0
  60. package/dist/src/narrative/index.js.map +1 -0
  61. package/dist/src/narrative/markdown-export.d.ts.map +1 -0
  62. package/dist/src/narrative/markdown-export.js.map +1 -0
  63. package/dist/src/narrative/tool-definitions.d.ts.map +1 -0
  64. package/dist/src/narrative/tool-definitions.js.map +1 -0
  65. package/dist/src/narrative/tool-handlers.d.ts.map +1 -0
  66. package/dist/src/narrative/tool-handlers.js.map +1 -0
  67. package/dist/src/narrative/types.d.ts.map +1 -0
  68. package/dist/src/narrative/types.js.map +1 -0
  69. package/dist/src/narrative/validation.d.ts.map +1 -0
  70. package/dist/src/narrative/validation.js.map +1 -0
  71. package/dist/src/pde/index.d.ts.map +1 -0
  72. package/dist/src/pde/index.js.map +1 -0
  73. package/dist/src/pde/mcp-handlers.d.ts.map +1 -0
  74. package/dist/src/pde/mcp-handlers.js.map +1 -0
  75. package/dist/src/pde/mcp-tools.d.ts.map +1 -0
  76. package/dist/src/pde/mcp-tools.js.map +1 -0
  77. package/dist/src/pde/session-manager.d.ts.map +1 -0
  78. package/dist/src/pde/session-manager.js.map +1 -0
  79. package/dist/src/pde/stc-mapper.d.ts.map +1 -0
  80. package/dist/src/pde/stc-mapper.js.map +1 -0
  81. package/dist/src/pipeline/index.d.ts.map +1 -0
  82. package/dist/src/pipeline/index.js.map +1 -0
  83. package/dist/src/pipeline/template-engine.d.ts.map +1 -0
  84. package/dist/src/pipeline/template-engine.js.map +1 -0
  85. package/dist/src/planning/index.d.ts.map +1 -0
  86. package/dist/src/planning/index.js.map +1 -0
  87. package/dist/src/planning/mcp-handlers.d.ts.map +1 -0
  88. package/dist/src/planning/mcp-handlers.js.map +1 -0
  89. package/dist/src/planning/mcp-tools.d.ts.map +1 -0
  90. package/dist/src/planning/mcp-tools.js.map +1 -0
  91. package/dist/src/planning/plan-parser.d.ts.map +1 -0
  92. package/dist/src/planning/plan-parser.js.map +1 -0
  93. package/dist/src/redis.d.ts.map +1 -0
  94. package/dist/src/redis.js.map +1 -0
  95. package/dist/src/types.d.ts.map +1 -0
  96. package/dist/src/types.js.map +1 -0
  97. package/dist/src/version.d.ts.map +1 -0
  98. package/dist/src/version.js.map +1 -0
  99. package/llms-full.txt +361 -0
  100. package/llms.txt +43 -0
  101. package/package.json +79 -12
  102. package/.claude/settings.local.json +0 -17
  103. package/.claude-plugin/marketplace.json +0 -26
  104. package/articles/academic/NOTES.md +0 -1
  105. package/articles/academic/README.md +0 -1
  106. package/articles/academic/creative-orientation-vs-problem-solving.md +0 -177
  107. package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +0 -142
  108. package/articles/academic/langfuse-observability-llm-pipelines.md +0 -144
  109. package/articles/academic/medicine-wheel-software-architecture.md +0 -163
  110. package/articles/academic/mmot-autonomous-agents.md +0 -156
  111. package/articles/academic/model-context-protocol-interagent.md +0 -161
  112. package/articles/academic/pde-prompt-decomposition.md +0 -186
  113. package/articles/academic/structural-tension-in-ai-agents.md +0 -134
  114. package/articles/reviews/mcp-protocol-design-review.md +0 -170
  115. package/articles/reviews/observability-ai-systems-review.md +0 -176
  116. package/articles/reviews/prompt-engineering-decomposition-review.md +0 -184
  117. package/articles/surveys/agent-orchestration-survey.md +0 -186
  118. package/articles/surveys/knowledge-graph-storage-survey.md +0 -204
  119. package/articles/surveys/structural-tension-methodology-survey.md +0 -154
  120. package/articles/technical/aws-sdk-v3-polly.md +0 -270
  121. package/articles/technical/commander-cli-framework.md +0 -262
  122. package/articles/technical/dotenv-config-patterns.md +0 -360
  123. package/articles/technical/ioredis-vs-redis.md +0 -142
  124. package/articles/technical/langfuse-js-sdk-vs-rest.md +0 -191
  125. package/articles/technical/mcp-sdk-typescript.md +0 -291
  126. package/articles/technical/octokit-github-api.md +0 -293
  127. package/articles/technical/openai-sdk-modern.md +0 -231
  128. package/articles/technical/yaml-parsing-node.md +0 -266
  129. package/articles/technical/zod-runtime-validation.md +0 -212
  130. package/coaiajs-langfuse-claude-plugin/.claude-plugin/marketplace.json +0 -16
  131. package/coaiajs-langfuse-claude-plugin/.claude-plugin/plugin.json +0 -10
  132. package/coaiajs-langfuse-claude-plugin/.mcp.json +0 -8
  133. package/coaiajs-langfuse-claude-plugin/README.md +0 -60
  134. package/mcp/config.ts +0 -225
  135. package/mcp/prompts.ts +0 -131
  136. package/mcp/resources.ts +0 -84
  137. package/mcp/server.ts +0 -519
  138. package/mcp/tools/coaiapy-tools.ts +0 -366
  139. package/mcp/tools/index.ts +0 -4
  140. package/src/audio.ts +0 -76
  141. package/src/cli-helpers.ts +0 -86
  142. package/src/cli.ts +0 -1261
  143. package/src/config.ts +0 -207
  144. package/src/environment.ts +0 -171
  145. package/src/github.ts +0 -143
  146. package/src/index.ts +0 -82
  147. package/src/langfuse/client.ts +0 -105
  148. package/src/langfuse/comments.ts +0 -52
  149. package/src/langfuse/datasets.ts +0 -178
  150. package/src/langfuse/index.ts +0 -37
  151. package/src/langfuse/media.ts +0 -193
  152. package/src/langfuse/observations.ts +0 -155
  153. package/src/langfuse/prompts.ts +0 -157
  154. package/src/langfuse/scores.ts +0 -456
  155. package/src/langfuse/traces.ts +0 -353
  156. package/src/llm.ts +0 -106
  157. package/src/narrative/graph-manager.ts +0 -1358
  158. package/src/narrative/index.ts +0 -188
  159. package/src/narrative/markdown-export.ts +0 -535
  160. package/src/narrative/tool-definitions.ts +0 -635
  161. package/src/narrative/tool-handlers.ts +0 -528
  162. package/src/narrative/types.ts +0 -9
  163. package/src/narrative/validation.ts +0 -179
  164. package/src/pde/index.ts +0 -34
  165. package/src/pde/mcp-handlers.ts +0 -359
  166. package/src/pde/mcp-tools.ts +0 -201
  167. package/src/pde/session-manager.ts +0 -248
  168. package/src/pde/stc-mapper.ts +0 -298
  169. package/src/pipeline/index.ts +0 -7
  170. package/src/pipeline/template-engine.ts +0 -398
  171. package/src/planning/index.ts +0 -32
  172. package/src/planning/mcp-handlers.ts +0 -369
  173. package/src/planning/mcp-tools.ts +0 -155
  174. package/src/planning/plan-parser.ts +0 -587
  175. package/src/redis.ts +0 -111
  176. package/src/types.ts +0 -281
  177. package/src/version.ts +0 -39
  178. package/test/config.test.mjs +0 -93
  179. package/tsconfig.json +0 -26
@@ -1,157 +0,0 @@
1
- // coaiajs/src/langfuse/prompts.ts — Prompt operations
2
- // Port of cofuse.py prompt functions
3
-
4
- import { getClient } from './client.js';
5
-
6
- export async function listPrompts(): Promise<string> {
7
- const client = getClient();
8
- const allPrompts: unknown[] = [];
9
- let page = 1;
10
-
11
- while (true) {
12
- const data = await client.request<Record<string, unknown>>(
13
- 'GET',
14
- `/api/public/v2/prompts?page=${page}`,
15
- );
16
-
17
- const prompts = (data as Record<string, unknown>).data;
18
- if (!prompts || !Array.isArray(prompts) || prompts.length === 0) break;
19
- allPrompts.push(...prompts);
20
-
21
- const meta = (data as Record<string, unknown>).meta as Record<string, unknown> | undefined;
22
- if (meta?.totalPages && (meta.page as number ?? page) >= (meta.totalPages as number)) break;
23
- if (!(data as Record<string, unknown>).hasNextPage && !meta?.totalPages) break;
24
- page++;
25
- }
26
-
27
- return JSON.stringify(allPrompts, null, 2);
28
- }
29
-
30
- export async function getPrompt(name: string, label?: string): Promise<string> {
31
- const client = getClient();
32
- const params = label ? `?label=${encodeURIComponent(label)}` : '';
33
- const result = await client.request<unknown>(
34
- 'GET',
35
- `/api/public/v2/prompts/${encodeURIComponent(name)}${params}`,
36
- );
37
- return JSON.stringify(result, null, 2);
38
- }
39
-
40
- export async function createPrompt(params: {
41
- name: string;
42
- content: string | unknown[];
43
- commitMessage?: string;
44
- labels?: string[];
45
- tags?: string[];
46
- promptType?: 'text' | 'chat';
47
- config?: Record<string, unknown>;
48
- }): Promise<string> {
49
- const client = getClient();
50
-
51
- const data: Record<string, unknown> = {
52
- type: params.promptType ?? 'text',
53
- name: params.name,
54
- prompt: params.content,
55
- };
56
-
57
- if (params.commitMessage) data.commitMessage = params.commitMessage;
58
- if (params.labels) data.labels = params.labels;
59
- if (params.tags) data.tags = params.tags;
60
- if (params.config) data.config = params.config;
61
-
62
- const result = await client.request<unknown>('POST', '/api/public/v2/prompts', data);
63
- return JSON.stringify(result, null, 2);
64
- }
65
-
66
- // ─── Formatters ─────────────────────────────────────────────────────
67
-
68
- export function formatPromptsTable(json: unknown): string {
69
- try {
70
- const raw = typeof json === 'string' ? JSON.parse(json) : json;
71
- const prompts: Record<string, unknown>[] = Array.isArray(raw)
72
- ? raw
73
- : (raw as Record<string, unknown>).data as Record<string, unknown>[] ?? [];
74
-
75
- if (!prompts.length) return 'No prompts found.';
76
-
77
- const headers = ['Name', 'Version', 'Created', 'Tags/Labels'];
78
- const rows = prompts.map((p) => [
79
- trunc(String(p.name ?? 'N/A'), 30),
80
- String(p.version ?? 'N/A'),
81
- String(p.createdAt ?? 'N/A').slice(0, 10),
82
- ((p.labels as string[]) ?? []).join(', ') || 'None',
83
- ]);
84
-
85
- return formatTable(headers, rows) + `\nTotal prompts: ${prompts.length}`;
86
- } catch (e) {
87
- return `Error formatting prompts: ${e}`;
88
- }
89
- }
90
-
91
- export function formatPromptDisplay(json: unknown): string {
92
- try {
93
- const prompt = (typeof json === 'string' ? JSON.parse(json) : json) as Record<string, unknown>;
94
- if ('error' in prompt) return `Error: ${prompt.error}`;
95
-
96
- const name = String(prompt.name ?? 'Unnamed');
97
- const version = String(prompt.version ?? 'N/A');
98
- const type = String(prompt.type ?? 'text');
99
- const isActive = Boolean(prompt.isActive);
100
- const created = String(prompt.createdAt ?? 'N/A').slice(0, 19);
101
- const updated = String(prompt.updatedAt ?? 'N/A').slice(0, 19);
102
- const labels = (prompt.labels as string[]) ?? [];
103
- const tags = (prompt.tags as string[]) ?? [];
104
- const commitMsg = String(prompt.commitMessage ?? '');
105
- const config = prompt.config as Record<string, unknown> | undefined;
106
- const content = String(prompt.prompt ?? '');
107
-
108
- const header = `🎯 PROMPT: ${name}${version !== 'N/A' ? ` (v${version})` : ''}`;
109
- const sep = '='.repeat(header.length);
110
-
111
- const lines = [sep, header, sep, ''];
112
- lines.push('📋 METADATA:');
113
- lines.push(` Type: ${type}`);
114
- lines.push(` Active: ${isActive ? '✅ Yes' : '❌ No'}`);
115
- lines.push(` Created: ${created}`);
116
- lines.push(` Updated: ${updated}`);
117
- lines.push(` Labels: ${labels.length ? labels.join(', ') : 'None'}`);
118
- if (tags.length) lines.push(` Tags: ${tags.join(', ')}`);
119
- if (commitMsg) lines.push(` Commit: ${commitMsg}`);
120
- lines.push('');
121
-
122
- if (config && Object.keys(config).length) {
123
- lines.push('⚙️ CONFIGURATION:');
124
- for (const [k, v] of Object.entries(config)) {
125
- lines.push(` ${k}: ${v}`);
126
- }
127
- lines.push('');
128
- }
129
-
130
- lines.push('📝 PROMPT CONTENT:');
131
- lines.push('-'.repeat(50));
132
- lines.push(content || '(No content)');
133
- lines.push('-'.repeat(50));
134
-
135
- return lines.join('\n');
136
- } catch (e) {
137
- return `Error formatting prompt: ${e}`;
138
- }
139
- }
140
-
141
- // ─── Helpers ────────────────────────────────────────────────────────
142
-
143
- function trunc(s: string, len: number): string {
144
- return s.length > len ? s.slice(0, len - 3) + '...' : s;
145
- }
146
-
147
- function formatTable(headers: string[], rows: string[][]): string {
148
- const widths = headers.map((h, i) =>
149
- Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)),
150
- );
151
- const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
152
- const hdr = '| ' + headers.map((h, i) => h.padEnd(widths[i])).join(' | ') + ' |';
153
- const body = rows.map(
154
- (r) => '| ' + r.map((c, i) => (c ?? '').padEnd(widths[i])).join(' | ') + ' |',
155
- );
156
- return [sep, hdr, sep, ...body, sep].join('\n');
157
- }
@@ -1,456 +0,0 @@
1
- // coaiajs/src/langfuse/scores.ts — Score operations
2
- // Port of cofuse.py score functions including configs, presets, import/export
3
-
4
- import { readFileSync, writeFileSync } from 'node:fs';
5
- import { getClient, nowISO } from './client.js';
6
- import type { ScoreConfig } from '../types.js';
7
-
8
- export interface ScoreFilters {
9
- userId?: string;
10
- name?: string;
11
- fromTimestamp?: string;
12
- toTimestamp?: string;
13
- configId?: string;
14
- page?: number;
15
- }
16
-
17
- // ─── Score CRUD ─────────────────────────────────────────────────────
18
-
19
- export async function createScore(params: {
20
- id?: string;
21
- name?: string;
22
- value: number;
23
- traceId?: string;
24
- observationId?: string;
25
- configId?: string;
26
- comment?: string;
27
- }): Promise<string> {
28
- const client = getClient();
29
- const data: Record<string, unknown> = { value: params.value };
30
-
31
- if (params.id) data.id = params.id;
32
- if (params.name) data.name = params.name;
33
- if (params.traceId) data.traceId = params.traceId;
34
- if (params.observationId) data.observationId = params.observationId;
35
- if (params.configId) data.configId = params.configId;
36
- if (params.comment) data.comment = params.comment;
37
-
38
- const result = await client.request<unknown>('POST', '/api/public/scores', data);
39
- return JSON.stringify(result, null, 2);
40
- }
41
-
42
- export async function applyScoreToTrace(
43
- traceId: string,
44
- scoreId: string,
45
- value: number,
46
- ): Promise<string> {
47
- return createScoreForTarget({
48
- targetType: 'trace',
49
- targetId: traceId,
50
- scoreId,
51
- value,
52
- });
53
- }
54
-
55
- export async function createScoreForTarget(params: {
56
- targetType: 'trace' | 'session';
57
- targetId: string;
58
- scoreId?: string;
59
- value: number;
60
- scoreName?: string;
61
- observationId?: string;
62
- configId?: string;
63
- comment?: string;
64
- }): Promise<string> {
65
- const client = getClient();
66
- const data: Record<string, unknown> = { value: params.value };
67
-
68
- if (params.targetType === 'trace') {
69
- data.traceId = params.targetId;
70
- if (params.observationId) data.observationId = params.observationId;
71
- } else {
72
- data.sessionId = params.targetId;
73
- }
74
-
75
- if (params.configId) {
76
- data.configId = params.configId;
77
- } else {
78
- if (params.scoreId) data.id = params.scoreId;
79
- if (params.scoreName) data.name = params.scoreName;
80
- }
81
-
82
- if (params.comment) data.comment = params.comment;
83
-
84
- const result = await client.request<unknown>('POST', '/api/public/scores', data);
85
- return JSON.stringify(result, null, 2);
86
- }
87
-
88
- export async function listScores(filters: ScoreFilters): Promise<string> {
89
- const client = getClient();
90
- const allScores: unknown[] = [];
91
- let page = filters.page ?? 1;
92
-
93
- while (true) {
94
- const params = new URLSearchParams({ page: String(page) });
95
- if (filters.userId) params.set('userId', filters.userId);
96
- if (filters.name) params.set('name', filters.name);
97
- if (filters.fromTimestamp) params.set('fromTimestamp', filters.fromTimestamp);
98
- if (filters.toTimestamp) params.set('toTimestamp', filters.toTimestamp);
99
- if (filters.configId) params.set('configId', filters.configId);
100
-
101
- const data = await client.request<Record<string, unknown>>(
102
- 'GET',
103
- `/api/public/v2/scores?${params.toString()}`,
104
- );
105
-
106
- const scores = (data as Record<string, unknown>).data;
107
- if (!scores || !Array.isArray(scores) || scores.length === 0) break;
108
- allScores.push(...scores);
109
-
110
- if (!shouldContinuePagination(data, page)) break;
111
- page++;
112
- }
113
-
114
- return JSON.stringify(allScores, null, 2);
115
- }
116
-
117
- // ─── Score Configs ──────────────────────────────────────────────────
118
-
119
- export async function listScoreConfigs(): Promise<string> {
120
- const client = getClient();
121
- const allConfigs: unknown[] = [];
122
- let page = 1;
123
-
124
- while (true) {
125
- const data = await client.request<Record<string, unknown>>(
126
- 'GET',
127
- `/api/public/score-configs?page=${page}`,
128
- );
129
-
130
- const configs = (data as Record<string, unknown>).data;
131
- if (!configs || !Array.isArray(configs) || configs.length === 0) break;
132
- allConfigs.push(...configs);
133
-
134
- if (!shouldContinuePagination(data, page)) break;
135
- page++;
136
- }
137
-
138
- return JSON.stringify(allConfigs, null, 2);
139
- }
140
-
141
- export async function getScoreConfig(configId: string): Promise<string> {
142
- const client = getClient();
143
- const result = await client.request<unknown>('GET', `/api/public/score-configs/${configId}`);
144
- return JSON.stringify(result, null, 2);
145
- }
146
-
147
- export async function createScoreConfig(params: {
148
- name: string;
149
- dataType: 'NUMERIC' | 'CATEGORICAL' | 'BOOLEAN';
150
- description?: string;
151
- categories?: Array<{ label: string; value: number }>;
152
- minValue?: number;
153
- maxValue?: number;
154
- }): Promise<string> {
155
- const client = getClient();
156
- const data: Record<string, unknown> = {
157
- name: params.name,
158
- dataType: params.dataType,
159
- };
160
-
161
- if (params.description) data.description = params.description;
162
- if (params.categories) data.categories = params.categories;
163
- if (params.minValue !== undefined) data.minValue = params.minValue;
164
- if (params.maxValue !== undefined) data.maxValue = params.maxValue;
165
-
166
- const result = await client.request<unknown>('POST', '/api/public/score-configs', data);
167
- return JSON.stringify(result, null, 2);
168
- }
169
-
170
- export async function exportScoreConfigs(outputFile: string): Promise<string> {
171
- const configsRaw = await listScoreConfigs();
172
- const configs = JSON.parse(configsRaw) as Record<string, unknown>[];
173
-
174
- const exported = configs.map((c) => ({
175
- name: c.name,
176
- dataType: c.dataType,
177
- description: c.description,
178
- categories: c.categories,
179
- minValue: c.minValue,
180
- maxValue: c.maxValue,
181
- metadata: {
182
- id: c.id,
183
- createdAt: c.createdAt,
184
- updatedAt: c.updatedAt,
185
- projectId: c.projectId,
186
- isArchived: c.isArchived,
187
- },
188
- }));
189
-
190
- const exportData = {
191
- version: '1.0',
192
- exportedAt: nowISO(),
193
- totalConfigs: exported.length,
194
- configs: exported,
195
- };
196
-
197
- const json = JSON.stringify(exportData, null, 2);
198
- if (outputFile) {
199
- writeFileSync(outputFile, json, 'utf-8');
200
- }
201
- return json;
202
- }
203
-
204
- export async function importScoreConfigs(
205
- importFile: string,
206
- options?: { allowDuplicates?: boolean; selectedConfigs?: string[] },
207
- ): Promise<string> {
208
- let importData: Record<string, unknown>;
209
- try {
210
- importData = JSON.parse(readFileSync(importFile, 'utf-8')) as Record<string, unknown>;
211
- } catch (e) {
212
- return `❌ Failed to read import file: ${e}`;
213
- }
214
-
215
- const configsList = Array.isArray(importData)
216
- ? importData
217
- : (importData.configs as unknown[]) ?? [];
218
-
219
- if (!configsList.length) return '❌ No score configs found in import file.';
220
-
221
- const existingRaw = await listScoreConfigs();
222
- const existing = JSON.parse(existingRaw) as Record<string, unknown>[];
223
- const existingNames = new Set(existing.map((c) => String(c.name).toLowerCase()));
224
-
225
- const selected = options?.selectedConfigs
226
- ? new Set(options.selectedConfigs.map((n) => n.toLowerCase()))
227
- : null;
228
-
229
- const results: string[] = [];
230
- let imported = 0;
231
- let skipped = 0;
232
-
233
- for (const raw of configsList) {
234
- const cfg = raw as Record<string, unknown>;
235
- const name = String(cfg.name);
236
-
237
- if (selected && !selected.has(name.toLowerCase())) continue;
238
- if (existingNames.has(name.toLowerCase()) && !options?.allowDuplicates) {
239
- results.push(` ⏭️ '${name}' — skipped (duplicate)`);
240
- skipped++;
241
- continue;
242
- }
243
-
244
- try {
245
- const result = await createScoreConfig({
246
- name,
247
- dataType: cfg.dataType as 'NUMERIC' | 'CATEGORICAL' | 'BOOLEAN',
248
- description: cfg.description as string | undefined,
249
- categories: cfg.categories as Array<{ label: string; value: number }> | undefined,
250
- minValue: cfg.minValue as number | undefined,
251
- maxValue: cfg.maxValue as number | undefined,
252
- });
253
- const parsed = JSON.parse(result);
254
- results.push(` ✅ '${name}' (ID: ${parsed.id ?? 'created'})`);
255
- imported++;
256
- } catch (e) {
257
- results.push(` ❌ '${name}' — ${e}`);
258
- }
259
- }
260
-
261
- return [
262
- `📁 Import: ${importFile}`,
263
- `📊 Summary: ${imported} imported, ${skipped} skipped`,
264
- ...results,
265
- ].join('\n');
266
- }
267
-
268
- // ─── Score Config Caching / Apply ───────────────────────────────────
269
-
270
- export async function applyScoreConfig(
271
- configNameOrId: string,
272
- targetType: string,
273
- targetId: string,
274
- value: number,
275
- observationId?: string,
276
- comment?: string,
277
- ): Promise<string> {
278
- // Resolve config: try by ID first, then search by name
279
- let configId: string | undefined;
280
-
281
- try {
282
- const configJson = await getScoreConfig(configNameOrId);
283
- const config = JSON.parse(configJson);
284
- if (config.id) configId = config.id;
285
- } catch {
286
- // Try listing and searching by name
287
- const allJson = await listScoreConfigs();
288
- const all = JSON.parse(allJson) as Record<string, unknown>[];
289
- const match = all.find(
290
- (c) => String(c.name).toLowerCase() === configNameOrId.toLowerCase(),
291
- );
292
- if (match) configId = String(match.id);
293
- }
294
-
295
- if (!configId) {
296
- return JSON.stringify({ error: `Score config '${configNameOrId}' not found` });
297
- }
298
-
299
- return createScoreForTarget({
300
- targetType: targetType as 'trace' | 'session',
301
- targetId,
302
- configId,
303
- value,
304
- observationId,
305
- comment,
306
- });
307
- }
308
-
309
- // ─── Presets ────────────────────────────────────────────────────────
310
-
311
- export function getBuiltInPresets(): ScoreConfig[] {
312
- return BUILT_IN_PRESETS.map((p) => ({ ...p }));
313
- }
314
-
315
- export async function installPreset(
316
- name: string,
317
- checkDuplicates = true,
318
- ): Promise<string> {
319
- const preset = BUILT_IN_PRESETS.find((p) => p.name.toLowerCase() === name.toLowerCase());
320
- if (!preset) {
321
- return JSON.stringify({ error: `Preset '${name}' not found` });
322
- }
323
-
324
- if (checkDuplicates) {
325
- const existingRaw = await listScoreConfigs();
326
- const existing = JSON.parse(existingRaw) as Record<string, unknown>[];
327
- const dup = existing.find((c) => String(c.name).toLowerCase() === preset.name.toLowerCase());
328
- if (dup) {
329
- return JSON.stringify({
330
- warning: `Score config '${preset.name}' already exists (ID: ${dup.id})`,
331
- existingId: dup.id,
332
- });
333
- }
334
- }
335
-
336
- return createScoreConfig({
337
- name: preset.name,
338
- dataType: preset.dataType,
339
- description: preset.description,
340
- categories: preset.categories,
341
- minValue: preset.minValue,
342
- maxValue: preset.maxValue,
343
- });
344
- }
345
-
346
- // ─── Formatters ─────────────────────────────────────────────────────
347
-
348
- export function formatScoresTable(json: unknown): string {
349
- try {
350
- const raw = typeof json === 'string' ? JSON.parse(json) : json;
351
- const scores: Record<string, unknown>[] = Array.isArray(raw)
352
- ? raw
353
- : (raw as Record<string, unknown>).data as Record<string, unknown>[] ?? [];
354
-
355
- if (!scores.length) return 'No scores found.';
356
-
357
- const headers = ['ID', 'Name', 'Value', 'Created', 'Trace ID'];
358
- const rows = scores.map((s) => [
359
- trunc(String(s.id ?? 'N/A'), 20),
360
- trunc(String(s.name ?? 'N/A'), 20),
361
- String(s.value ?? 'N/A'),
362
- String(s.createdAt ?? 'N/A').slice(0, 16),
363
- trunc(String(s.traceId ?? 'N/A'), 20),
364
- ]);
365
-
366
- return formatTable(headers, rows) + `\nTotal scores: ${scores.length}`;
367
- } catch (e) {
368
- return `Error formatting scores: ${e}`;
369
- }
370
- }
371
-
372
- export function formatScoreConfigsTable(json: unknown): string {
373
- try {
374
- const raw = typeof json === 'string' ? JSON.parse(json) : json;
375
- const configs: Record<string, unknown>[] = Array.isArray(raw)
376
- ? raw
377
- : (raw as Record<string, unknown>).data as Record<string, unknown>[] ?? [];
378
-
379
- if (!configs.length) return 'No score configs found.';
380
-
381
- const headers = ['ID', 'Name', 'Data Type', 'Description', 'Created'];
382
- const rows = configs.map((c) => [
383
- trunc(String(c.id ?? 'N/A'), 20),
384
- trunc(String(c.name ?? 'N/A'), 20),
385
- String(c.dataType ?? 'N/A'),
386
- trunc(String(c.description ?? 'N/A'), 35),
387
- String(c.createdAt ?? 'N/A').slice(0, 16),
388
- ]);
389
-
390
- return formatTable(headers, rows) + `\nTotal score configs: ${configs.length}`;
391
- } catch (e) {
392
- return `Error formatting score configs: ${e}`;
393
- }
394
- }
395
-
396
- // ─── Built-in Presets ───────────────────────────────────────────────
397
-
398
- const BUILT_IN_PRESETS: ScoreConfig[] = [
399
- // Narrative & Storytelling
400
- { 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 }] },
401
- { 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 }] },
402
- { 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 }] },
403
- { 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 }] },
404
- { 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 }] },
405
- // AI Response Evaluation
406
- { 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 }] },
407
- { 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 }] },
408
- { 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 }] },
409
- { 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 }] },
410
- { 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 }] },
411
- // General Content
412
- { 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 }] },
413
- { 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 }] },
414
- { 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 }] },
415
- { 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 }] },
416
- // Technical Quality
417
- { 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 }] },
418
- { 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 }] },
419
- // Specialized
420
- { 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 }] },
421
- { 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 }] },
422
- { 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 }] },
423
- // Numeric
424
- { name: 'Overall Quality', dataType: 'NUMERIC', description: 'General numeric assessment of overall content quality.', minValue: 0, maxValue: 10 },
425
- { name: 'Performance Score', dataType: 'NUMERIC', description: 'Numeric performance evaluation score.', minValue: 0, maxValue: 100 },
426
- // Boolean
427
- { name: 'Meets Requirements', dataType: 'BOOLEAN', description: 'Binary assessment of whether content meets specified requirements.' },
428
- ];
429
-
430
- // ─── Helpers ────────────────────────────────────────────────────────
431
-
432
- function shouldContinuePagination(data: Record<string, unknown>, currentPage: number): boolean {
433
- const meta = data.meta as Record<string, unknown> | undefined;
434
- if (meta?.totalPages) {
435
- return (meta.page as number ?? currentPage) < (meta.totalPages as number);
436
- }
437
- if (data.hasNextPage) return true;
438
- if (data.totalPages && currentPage < (data.totalPages as number)) return true;
439
- return false;
440
- }
441
-
442
- function trunc(s: string, len: number): string {
443
- return s.length > len ? s.slice(0, len - 3) + '...' : s;
444
- }
445
-
446
- function formatTable(headers: string[], rows: string[][]): string {
447
- const widths = headers.map((h, i) =>
448
- Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)),
449
- );
450
- const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
451
- const hdr = '| ' + headers.map((h, i) => h.padEnd(widths[i])).join(' | ') + ' |';
452
- const body = rows.map(
453
- (r) => '| ' + r.map((c, i) => (c ?? '').padEnd(widths[i])).join(' | ') + ' |',
454
- );
455
- return [sep, hdr, sep, ...body, sep].join('\n');
456
- }