coaiajs 0.3.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) 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.map +1 -0
  23. package/dist/src/config.d.ts.map +1 -0
  24. package/dist/src/config.js.map +1 -0
  25. package/dist/src/environment.d.ts.map +1 -0
  26. package/dist/src/environment.js.map +1 -0
  27. package/dist/src/github.d.ts.map +1 -0
  28. package/dist/src/github.js.map +1 -0
  29. package/dist/src/index.d.ts +1 -0
  30. package/dist/src/index.d.ts.map +1 -0
  31. package/dist/src/index.js +1 -0
  32. package/dist/src/index.js.map +1 -0
  33. package/dist/src/langfuse/client.d.ts.map +1 -0
  34. package/dist/src/langfuse/client.js.map +1 -0
  35. package/dist/src/langfuse/comments.d.ts.map +1 -0
  36. package/dist/src/langfuse/comments.js.map +1 -0
  37. package/dist/src/langfuse/datasets.d.ts.map +1 -0
  38. package/dist/src/langfuse/datasets.js.map +1 -0
  39. package/dist/src/langfuse/index.d.ts.map +1 -0
  40. package/dist/src/langfuse/index.js.map +1 -0
  41. package/dist/src/langfuse/media.d.ts.map +1 -0
  42. package/dist/src/langfuse/media.js.map +1 -0
  43. package/dist/src/langfuse/observations.d.ts.map +1 -0
  44. package/dist/src/langfuse/observations.js.map +1 -0
  45. package/dist/src/langfuse/prompts.d.ts.map +1 -0
  46. package/dist/src/langfuse/prompts.js.map +1 -0
  47. package/dist/src/langfuse/scores.d.ts.map +1 -0
  48. package/dist/src/langfuse/scores.js.map +1 -0
  49. package/dist/src/langfuse/traces.d.ts.map +1 -0
  50. package/dist/src/langfuse/traces.js.map +1 -0
  51. package/dist/src/llm.d.ts.map +1 -0
  52. package/dist/src/llm.js.map +1 -0
  53. package/dist/src/narrative/graph-manager.d.ts.map +1 -0
  54. package/dist/src/narrative/graph-manager.js.map +1 -0
  55. package/dist/src/narrative/index.d.ts.map +1 -0
  56. package/dist/src/narrative/index.js.map +1 -0
  57. package/dist/src/narrative/markdown-export.d.ts.map +1 -0
  58. package/dist/src/narrative/markdown-export.js.map +1 -0
  59. package/dist/src/narrative/tool-definitions.d.ts.map +1 -0
  60. package/dist/src/narrative/tool-definitions.js.map +1 -0
  61. package/dist/src/narrative/tool-handlers.d.ts.map +1 -0
  62. package/dist/src/narrative/tool-handlers.js.map +1 -0
  63. package/dist/src/narrative/types.d.ts.map +1 -0
  64. package/dist/src/narrative/types.js.map +1 -0
  65. package/dist/src/narrative/validation.d.ts.map +1 -0
  66. package/dist/src/narrative/validation.js.map +1 -0
  67. package/dist/src/pde/index.d.ts.map +1 -0
  68. package/dist/src/pde/index.js.map +1 -0
  69. package/dist/src/pde/mcp-handlers.d.ts.map +1 -0
  70. package/dist/src/pde/mcp-handlers.js.map +1 -0
  71. package/dist/src/pde/mcp-tools.d.ts.map +1 -0
  72. package/dist/src/pde/mcp-tools.js.map +1 -0
  73. package/dist/src/pde/session-manager.d.ts.map +1 -0
  74. package/dist/src/pde/session-manager.js.map +1 -0
  75. package/dist/src/pde/stc-mapper.d.ts.map +1 -0
  76. package/dist/src/pde/stc-mapper.js.map +1 -0
  77. package/dist/src/pipeline/index.d.ts.map +1 -0
  78. package/dist/src/pipeline/index.js.map +1 -0
  79. package/dist/src/pipeline/template-engine.d.ts.map +1 -0
  80. package/dist/src/pipeline/template-engine.js.map +1 -0
  81. package/dist/src/planning/index.d.ts.map +1 -0
  82. package/dist/src/planning/index.js.map +1 -0
  83. package/dist/src/planning/mcp-handlers.d.ts.map +1 -0
  84. package/dist/src/planning/mcp-handlers.js.map +1 -0
  85. package/dist/src/planning/mcp-tools.d.ts.map +1 -0
  86. package/dist/src/planning/mcp-tools.js.map +1 -0
  87. package/dist/src/planning/plan-parser.d.ts.map +1 -0
  88. package/dist/src/planning/plan-parser.js.map +1 -0
  89. package/dist/src/redis.d.ts.map +1 -0
  90. package/dist/src/redis.js.map +1 -0
  91. package/dist/src/types.d.ts.map +1 -0
  92. package/dist/src/types.js.map +1 -0
  93. package/dist/src/version.d.ts.map +1 -0
  94. package/dist/src/version.js.map +1 -0
  95. package/llms-full.txt +361 -0
  96. package/llms.txt +43 -0
  97. package/package.json +79 -12
  98. package/.claude/settings.local.json +0 -17
  99. package/.claude-plugin/marketplace.json +0 -26
  100. package/articles/academic/NOTES.md +0 -1
  101. package/articles/academic/README.md +0 -1
  102. package/articles/academic/creative-orientation-vs-problem-solving.md +0 -177
  103. package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +0 -142
  104. package/articles/academic/langfuse-observability-llm-pipelines.md +0 -144
  105. package/articles/academic/medicine-wheel-software-architecture.md +0 -163
  106. package/articles/academic/mmot-autonomous-agents.md +0 -156
  107. package/articles/academic/model-context-protocol-interagent.md +0 -161
  108. package/articles/academic/pde-prompt-decomposition.md +0 -186
  109. package/articles/academic/structural-tension-in-ai-agents.md +0 -134
  110. package/articles/reviews/mcp-protocol-design-review.md +0 -170
  111. package/articles/reviews/observability-ai-systems-review.md +0 -176
  112. package/articles/reviews/prompt-engineering-decomposition-review.md +0 -184
  113. package/articles/surveys/agent-orchestration-survey.md +0 -186
  114. package/articles/surveys/knowledge-graph-storage-survey.md +0 -204
  115. package/articles/surveys/structural-tension-methodology-survey.md +0 -154
  116. package/articles/technical/aws-sdk-v3-polly.md +0 -270
  117. package/articles/technical/commander-cli-framework.md +0 -262
  118. package/articles/technical/dotenv-config-patterns.md +0 -360
  119. package/articles/technical/ioredis-vs-redis.md +0 -142
  120. package/articles/technical/langfuse-js-sdk-vs-rest.md +0 -191
  121. package/articles/technical/mcp-sdk-typescript.md +0 -291
  122. package/articles/technical/octokit-github-api.md +0 -293
  123. package/articles/technical/openai-sdk-modern.md +0 -231
  124. package/articles/technical/yaml-parsing-node.md +0 -266
  125. package/articles/technical/zod-runtime-validation.md +0 -212
  126. package/coaiajs-langfuse-claude-plugin/.claude-plugin/marketplace.json +0 -16
  127. package/coaiajs-langfuse-claude-plugin/.claude-plugin/plugin.json +0 -10
  128. package/coaiajs-langfuse-claude-plugin/.mcp.json +0 -8
  129. package/coaiajs-langfuse-claude-plugin/README.md +0 -60
  130. package/mcp/config.ts +0 -225
  131. package/mcp/prompts.ts +0 -131
  132. package/mcp/resources.ts +0 -84
  133. package/mcp/server.ts +0 -519
  134. package/mcp/tools/coaiapy-tools.ts +0 -366
  135. package/mcp/tools/index.ts +0 -4
  136. package/src/audio.ts +0 -76
  137. package/src/cli-helpers.ts +0 -86
  138. package/src/cli.ts +0 -1261
  139. package/src/config.ts +0 -207
  140. package/src/environment.ts +0 -171
  141. package/src/github.ts +0 -143
  142. package/src/index.ts +0 -82
  143. package/src/langfuse/client.ts +0 -105
  144. package/src/langfuse/comments.ts +0 -52
  145. package/src/langfuse/datasets.ts +0 -178
  146. package/src/langfuse/index.ts +0 -37
  147. package/src/langfuse/media.ts +0 -193
  148. package/src/langfuse/observations.ts +0 -155
  149. package/src/langfuse/prompts.ts +0 -157
  150. package/src/langfuse/scores.ts +0 -456
  151. package/src/langfuse/traces.ts +0 -353
  152. package/src/llm.ts +0 -106
  153. package/src/narrative/graph-manager.ts +0 -1358
  154. package/src/narrative/index.ts +0 -188
  155. package/src/narrative/markdown-export.ts +0 -535
  156. package/src/narrative/tool-definitions.ts +0 -635
  157. package/src/narrative/tool-handlers.ts +0 -528
  158. package/src/narrative/types.ts +0 -9
  159. package/src/narrative/validation.ts +0 -179
  160. package/src/pde/index.ts +0 -34
  161. package/src/pde/mcp-handlers.ts +0 -359
  162. package/src/pde/mcp-tools.ts +0 -201
  163. package/src/pde/session-manager.ts +0 -248
  164. package/src/pde/stc-mapper.ts +0 -298
  165. package/src/pipeline/index.ts +0 -7
  166. package/src/pipeline/template-engine.ts +0 -398
  167. package/src/planning/index.ts +0 -32
  168. package/src/planning/mcp-handlers.ts +0 -369
  169. package/src/planning/mcp-tools.ts +0 -155
  170. package/src/planning/plan-parser.ts +0 -587
  171. package/src/redis.ts +0 -111
  172. package/src/types.ts +0 -281
  173. package/src/version.ts +0 -39
  174. package/test/config.test.mjs +0 -93
  175. 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
- }