coaiajs 0.1.0 → 0.1.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 (187) hide show
  1. package/README.md +7 -3
  2. package/dist/mcp/config.d.ts +4 -0
  3. package/dist/mcp/config.d.ts.map +1 -0
  4. package/dist/mcp/config.js +52 -26
  5. package/dist/mcp/config.js.map +1 -0
  6. package/dist/mcp/prompts.d.ts +19 -0
  7. package/dist/mcp/prompts.d.ts.map +1 -0
  8. package/dist/mcp/prompts.js +111 -0
  9. package/dist/mcp/prompts.js.map +1 -0
  10. package/dist/mcp/resources.d.ts +5 -0
  11. package/dist/mcp/resources.d.ts.map +1 -0
  12. package/dist/mcp/resources.js +77 -0
  13. package/dist/mcp/resources.js.map +1 -0
  14. package/dist/mcp/server.d.ts.map +1 -0
  15. package/dist/mcp/server.js +167 -75
  16. package/dist/mcp/server.js.map +1 -0
  17. package/dist/mcp/tools/coaiapy-tools.d.ts.map +1 -0
  18. package/dist/mcp/tools/coaiapy-tools.js +1 -0
  19. package/dist/mcp/tools/coaiapy-tools.js.map +1 -0
  20. package/dist/mcp/tools/index.d.ts.map +1 -0
  21. package/dist/mcp/tools/index.js.map +1 -0
  22. package/dist/src/audio.d.ts.map +1 -0
  23. package/dist/src/audio.js.map +1 -0
  24. package/dist/src/cli-helpers.d.ts.map +1 -0
  25. package/dist/src/cli-helpers.js.map +1 -0
  26. package/dist/src/cli.d.ts.map +1 -0
  27. package/dist/src/cli.js +4 -4
  28. package/dist/src/cli.js.map +1 -0
  29. package/dist/src/config.d.ts +2 -0
  30. package/dist/src/config.d.ts.map +1 -0
  31. package/dist/src/config.js +44 -14
  32. package/dist/src/config.js.map +1 -0
  33. package/dist/src/environment.d.ts.map +1 -0
  34. package/dist/src/environment.js.map +1 -0
  35. package/dist/src/github.d.ts.map +1 -0
  36. package/dist/src/github.js.map +1 -0
  37. package/dist/src/index.d.ts +13 -0
  38. package/dist/src/index.d.ts.map +1 -0
  39. package/dist/src/index.js +13 -0
  40. package/dist/src/index.js.map +1 -0
  41. package/dist/src/langfuse/client.d.ts.map +1 -0
  42. package/dist/src/langfuse/client.js.map +1 -0
  43. package/dist/src/langfuse/comments.d.ts.map +1 -0
  44. package/dist/src/langfuse/comments.js.map +1 -0
  45. package/dist/src/langfuse/datasets.d.ts.map +1 -0
  46. package/dist/src/langfuse/datasets.js.map +1 -0
  47. package/dist/src/langfuse/index.d.ts.map +1 -0
  48. package/dist/src/langfuse/index.js.map +1 -0
  49. package/dist/src/langfuse/media.d.ts.map +1 -0
  50. package/dist/src/langfuse/media.js.map +1 -0
  51. package/dist/src/langfuse/observations.d.ts.map +1 -0
  52. package/dist/src/langfuse/observations.js.map +1 -0
  53. package/dist/src/langfuse/prompts.d.ts.map +1 -0
  54. package/dist/src/langfuse/prompts.js.map +1 -0
  55. package/dist/src/langfuse/scores.d.ts.map +1 -0
  56. package/dist/src/langfuse/scores.js.map +1 -0
  57. package/dist/src/langfuse/traces.d.ts.map +1 -0
  58. package/dist/src/langfuse/traces.js.map +1 -0
  59. package/dist/src/llm.d.ts.map +1 -0
  60. package/dist/src/llm.js.map +1 -0
  61. package/dist/src/narrative/graph-manager.d.ts.map +1 -0
  62. package/dist/src/narrative/graph-manager.js.map +1 -0
  63. package/dist/src/narrative/index.d.ts +32 -1
  64. package/dist/src/narrative/index.d.ts.map +1 -0
  65. package/dist/src/narrative/index.js +97 -1
  66. package/dist/src/narrative/index.js.map +1 -0
  67. package/dist/src/narrative/markdown-export.d.ts.map +1 -0
  68. package/dist/src/narrative/markdown-export.js.map +1 -0
  69. package/dist/src/narrative/tool-definitions.d.ts.map +1 -0
  70. package/dist/src/narrative/tool-definitions.js.map +1 -0
  71. package/dist/src/narrative/tool-handlers.d.ts.map +1 -0
  72. package/dist/src/narrative/tool-handlers.js.map +1 -0
  73. package/dist/src/narrative/types.d.ts.map +1 -0
  74. package/dist/src/narrative/types.js.map +1 -0
  75. package/dist/src/narrative/validation.d.ts.map +1 -0
  76. package/dist/src/narrative/validation.js.map +1 -0
  77. package/dist/src/pde/index.d.ts +4 -0
  78. package/dist/src/pde/index.d.ts.map +1 -0
  79. package/dist/src/pde/index.js +19 -0
  80. package/dist/src/pde/index.js.map +1 -0
  81. package/dist/src/pde/mcp-handlers.d.ts.map +1 -0
  82. package/dist/src/pde/mcp-handlers.js.map +1 -0
  83. package/dist/src/pde/mcp-tools.d.ts.map +1 -0
  84. package/dist/src/pde/mcp-tools.js.map +1 -0
  85. package/dist/src/pde/session-manager.d.ts.map +1 -0
  86. package/dist/src/pde/session-manager.js.map +1 -0
  87. package/dist/src/pde/stc-mapper.d.ts.map +1 -0
  88. package/dist/src/pde/stc-mapper.js.map +1 -0
  89. package/dist/src/pipeline/index.d.ts.map +1 -0
  90. package/dist/src/pipeline/index.js.map +1 -0
  91. package/dist/src/pipeline/template-engine.d.ts.map +1 -0
  92. package/dist/src/pipeline/template-engine.js.map +1 -0
  93. package/dist/src/planning/index.d.ts +3 -0
  94. package/dist/src/planning/index.d.ts.map +1 -0
  95. package/dist/src/planning/index.js +13 -0
  96. package/dist/src/planning/index.js.map +1 -0
  97. package/dist/src/planning/mcp-handlers.d.ts.map +1 -0
  98. package/dist/src/planning/mcp-handlers.js.map +1 -0
  99. package/dist/src/planning/mcp-tools.d.ts.map +1 -0
  100. package/dist/src/planning/mcp-tools.js.map +1 -0
  101. package/dist/src/planning/plan-parser.d.ts.map +1 -0
  102. package/dist/src/planning/plan-parser.js.map +1 -0
  103. package/dist/src/redis.d.ts.map +1 -0
  104. package/dist/src/redis.js +20 -8
  105. package/dist/src/redis.js.map +1 -0
  106. package/dist/src/types.d.ts +1 -0
  107. package/dist/src/types.d.ts.map +1 -0
  108. package/dist/src/types.js.map +1 -0
  109. package/package.json +67 -2
  110. package/rispecs/00-coaiajs-platform.spec.md +23 -10
  111. package/rispecs/01-core-config.spec.md +7 -37
  112. package/rispecs/02-redis-module.spec.md +2 -1
  113. package/rispecs/03-langfuse-module.spec.md +7 -5
  114. package/rispecs/04-narrative-engine.spec.md +9 -9
  115. package/rispecs/05-pde-engine.spec.md +23 -23
  116. package/rispecs/06-planning-engine.spec.md +14 -13
  117. package/rispecs/07-pipeline-templates.spec.md +7 -7
  118. package/rispecs/08-cli-interface.spec.md +13 -25
  119. package/rispecs/09-mcp-server.spec.md +57 -62
  120. package/rispecs/10-audio-module.spec.md +3 -3
  121. package/rispecs/KINSHIP.md +4 -4
  122. package/rispecs/README.md +33 -7
  123. package/articles/academic/creative-orientation-vs-problem-solving.md +0 -177
  124. package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +0 -142
  125. package/articles/academic/langfuse-observability-llm-pipelines.md +0 -144
  126. package/articles/academic/medicine-wheel-software-architecture.md +0 -163
  127. package/articles/academic/mmot-autonomous-agents.md +0 -156
  128. package/articles/academic/model-context-protocol-interagent.md +0 -161
  129. package/articles/academic/pde-prompt-decomposition.md +0 -186
  130. package/articles/academic/structural-tension-in-ai-agents.md +0 -134
  131. package/articles/reviews/mcp-protocol-design-review.md +0 -170
  132. package/articles/reviews/observability-ai-systems-review.md +0 -176
  133. package/articles/reviews/prompt-engineering-decomposition-review.md +0 -184
  134. package/articles/surveys/agent-orchestration-survey.md +0 -186
  135. package/articles/surveys/knowledge-graph-storage-survey.md +0 -204
  136. package/articles/surveys/structural-tension-methodology-survey.md +0 -154
  137. package/articles/technical/aws-sdk-v3-polly.md +0 -270
  138. package/articles/technical/commander-cli-framework.md +0 -262
  139. package/articles/technical/dotenv-config-patterns.md +0 -360
  140. package/articles/technical/ioredis-vs-redis.md +0 -142
  141. package/articles/technical/langfuse-js-sdk-vs-rest.md +0 -191
  142. package/articles/technical/mcp-sdk-typescript.md +0 -291
  143. package/articles/technical/octokit-github-api.md +0 -293
  144. package/articles/technical/openai-sdk-modern.md +0 -231
  145. package/articles/technical/yaml-parsing-node.md +0 -266
  146. package/articles/technical/zod-runtime-validation.md +0 -212
  147. package/mcp/config.ts +0 -196
  148. package/mcp/server.ts +0 -402
  149. package/mcp/tools/coaiapy-tools.ts +0 -364
  150. package/mcp/tools/index.ts +0 -4
  151. package/src/audio.ts +0 -76
  152. package/src/cli-helpers.ts +0 -86
  153. package/src/cli.ts +0 -1223
  154. package/src/config.ts +0 -172
  155. package/src/environment.ts +0 -171
  156. package/src/github.ts +0 -143
  157. package/src/langfuse/client.ts +0 -105
  158. package/src/langfuse/comments.ts +0 -52
  159. package/src/langfuse/datasets.ts +0 -178
  160. package/src/langfuse/index.ts +0 -33
  161. package/src/langfuse/media.ts +0 -193
  162. package/src/langfuse/observations.ts +0 -131
  163. package/src/langfuse/prompts.ts +0 -157
  164. package/src/langfuse/scores.ts +0 -456
  165. package/src/langfuse/traces.ts +0 -276
  166. package/src/llm.ts +0 -106
  167. package/src/narrative/graph-manager.ts +0 -1358
  168. package/src/narrative/index.ts +0 -32
  169. package/src/narrative/markdown-export.ts +0 -535
  170. package/src/narrative/tool-definitions.ts +0 -635
  171. package/src/narrative/tool-handlers.ts +0 -528
  172. package/src/narrative/types.ts +0 -9
  173. package/src/narrative/validation.ts +0 -179
  174. package/src/pde/index.ts +0 -8
  175. package/src/pde/mcp-handlers.ts +0 -359
  176. package/src/pde/mcp-tools.ts +0 -201
  177. package/src/pde/session-manager.ts +0 -248
  178. package/src/pde/stc-mapper.ts +0 -298
  179. package/src/pipeline/index.ts +0 -7
  180. package/src/pipeline/template-engine.ts +0 -398
  181. package/src/planning/index.ts +0 -13
  182. package/src/planning/mcp-handlers.ts +0 -369
  183. package/src/planning/mcp-tools.ts +0 -155
  184. package/src/planning/plan-parser.ts +0 -587
  185. package/src/redis.ts +0 -97
  186. package/src/types.ts +0 -280
  187. package/tsconfig.json +0 -26
@@ -1,178 +0,0 @@
1
- // coaiajs/src/langfuse/datasets.ts — Dataset operations
2
- // Port of cofuse.py dataset functions
3
-
4
- import { getClient } from './client.js';
5
-
6
- export async function listDatasets(): Promise<string> {
7
- const client = getClient();
8
- const result = await client.request<unknown>('GET', '/api/public/v2/datasets');
9
- return JSON.stringify(result, null, 2);
10
- }
11
-
12
- export async function getDataset(name: string): Promise<string> {
13
- const client = getClient();
14
- const result = await client.request<unknown>(
15
- 'GET',
16
- `/api/public/v2/datasets/${encodeURIComponent(name)}`,
17
- );
18
- return JSON.stringify(result, null, 2);
19
- }
20
-
21
- export async function createDataset(params: {
22
- name: string;
23
- description?: string;
24
- metadata?: unknown;
25
- }): Promise<string> {
26
- const client = getClient();
27
- const data: Record<string, unknown> = { name: params.name };
28
-
29
- if (params.description) data.description = params.description;
30
- if (params.metadata) {
31
- data.metadata = typeof params.metadata === 'string'
32
- ? safeParseJSON(params.metadata)
33
- : params.metadata;
34
- }
35
-
36
- const result = await client.request<unknown>('POST', '/api/public/v2/datasets', data);
37
- return JSON.stringify(result, null, 2);
38
- }
39
-
40
- export async function listDatasetItems(name: string): Promise<string> {
41
- const client = getClient();
42
- const allItems: unknown[] = [];
43
- let page = 1;
44
-
45
- while (true) {
46
- const data = await client.request<Record<string, unknown>>(
47
- 'GET',
48
- `/api/public/dataset-items?name=${encodeURIComponent(name)}&page=${page}`,
49
- );
50
-
51
- const items = (data as Record<string, unknown>).data;
52
- if (!items || !Array.isArray(items) || items.length === 0) break;
53
- allItems.push(...items);
54
-
55
- const meta = (data as Record<string, unknown>).meta as Record<string, unknown> | undefined;
56
- if (!meta || (meta.page as number ?? page) >= (meta.totalPages as number ?? 1)) break;
57
- page++;
58
- }
59
-
60
- return JSON.stringify(allItems, null, 2);
61
- }
62
-
63
- export async function createDatasetItem(params: {
64
- datasetName: string;
65
- input: unknown;
66
- expectedOutput?: unknown;
67
- metadata?: unknown;
68
- sourceTraceId?: string;
69
- sourceObservationId?: string;
70
- }): Promise<string> {
71
- const client = getClient();
72
- const data: Record<string, unknown> = {
73
- datasetName: params.datasetName,
74
- input: params.input,
75
- };
76
-
77
- if (params.expectedOutput !== undefined) data.expectedOutput = params.expectedOutput;
78
- if (params.metadata) data.metadata = params.metadata;
79
- if (params.sourceTraceId) data.sourceTraceId = params.sourceTraceId;
80
- if (params.sourceObservationId) data.sourceObservationId = params.sourceObservationId;
81
-
82
- const result = await client.request<unknown>('POST', '/api/public/dataset-items', data);
83
- return JSON.stringify(result, null, 2);
84
- }
85
-
86
- // ─── Formatters ─────────────────────────────────────────────────────
87
-
88
- export function formatDatasetsTable(json: unknown): string {
89
- try {
90
- const raw = typeof json === 'string' ? JSON.parse(json) : json;
91
- const datasets: Record<string, unknown>[] = Array.isArray(raw)
92
- ? raw
93
- : (raw as Record<string, unknown>).data as Record<string, unknown>[] ?? [];
94
-
95
- if (!datasets.length) return 'No datasets found.';
96
-
97
- const headers = ['Name', 'Description', 'Items', 'Created'];
98
- const rows = datasets.map((d) => [
99
- trunc(String(d.name ?? 'N/A'), 25),
100
- trunc(String(d.description ?? 'N/A'), 30),
101
- String(d.items?.toString() ?? 'N/A'),
102
- String(d.createdAt ?? 'N/A').slice(0, 10),
103
- ]);
104
-
105
- return formatTable(headers, rows) + `\nTotal datasets: ${datasets.length}`;
106
- } catch (e) {
107
- return `Error formatting datasets: ${e}`;
108
- }
109
- }
110
-
111
- export function formatDatasetForFinetuning(
112
- items: unknown,
113
- format: 'openai' | 'gemini',
114
- systemInstruction?: string,
115
- ): string {
116
- try {
117
- const parsed = (typeof items === 'string' ? JSON.parse(items) : items) as Record<string, unknown>[];
118
- const sysInstr = systemInstruction ?? 'You are a helpful assistant.';
119
- const lines: string[] = [];
120
-
121
- for (const item of parsed) {
122
- const input = item.input;
123
- const output = item.expectedOutput;
124
- if (!input || !output) continue;
125
-
126
- if (format === 'openai') {
127
- lines.push(JSON.stringify({
128
- messages: [
129
- { role: 'system', content: sysInstr },
130
- { role: 'user', content: input },
131
- { role: 'assistant', content: output },
132
- ],
133
- }));
134
- } else {
135
- lines.push(JSON.stringify({
136
- systemInstruction: {
137
- role: 'system',
138
- parts: [{ text: sysInstr }],
139
- },
140
- contents: [
141
- { role: 'user', parts: [{ text: input }] },
142
- { role: 'model', parts: [{ text: output }] },
143
- ],
144
- }));
145
- }
146
- }
147
-
148
- return lines.join('\n');
149
- } catch (e) {
150
- return `Error formatting for fine-tuning: ${e}`;
151
- }
152
- }
153
-
154
- // ─── Helpers ────────────────────────────────────────────────────────
155
-
156
- function trunc(s: string, len: number): string {
157
- return s.length > len ? s.slice(0, len - 3) + '...' : s;
158
- }
159
-
160
- function safeParseJSON(s: string): unknown {
161
- try {
162
- return JSON.parse(s);
163
- } catch {
164
- return { note: s };
165
- }
166
- }
167
-
168
- function formatTable(headers: string[], rows: string[][]): string {
169
- const widths = headers.map((h, i) =>
170
- Math.max(h.length, ...rows.map((r) => (r[i] ?? '').length)),
171
- );
172
- const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
173
- const hdr = '| ' + headers.map((h, i) => h.padEnd(widths[i])).join(' | ') + ' |';
174
- const body = rows.map(
175
- (r) => '| ' + r.map((c, i) => (c ?? '').padEnd(widths[i])).join(' | ') + ' |',
176
- );
177
- return [sep, hdr, sep, ...body, sep].join('\n');
178
- }
@@ -1,33 +0,0 @@
1
- // coaiajs/src/langfuse/index.ts — Barrel export
2
-
3
- export { LangfuseClient, LangfuseApiError, getClient, resetClient, nowISO } from './client.js';
4
- export type { IngestionEvent, LangfuseClientConfig } from './client.js';
5
-
6
- export { addTrace, patchTraceOutput, listTraces, getTrace, formatTracesTable, formatTraceTree } from './traces.js';
7
- export type { TraceFilters } from './traces.js';
8
-
9
- export { addObservation, getObservation, formatObservationDisplay } from './observations.js';
10
-
11
- export { listPrompts, getPrompt, createPrompt, formatPromptsTable, formatPromptDisplay } from './prompts.js';
12
-
13
- export {
14
- listDatasets, getDataset, createDataset, listDatasetItems, createDatasetItem,
15
- formatDatasetsTable, formatDatasetForFinetuning,
16
- } from './datasets.js';
17
-
18
- export {
19
- createScore, applyScoreToTrace, createScoreForTarget, listScores,
20
- listScoreConfigs, getScoreConfig, createScoreConfig,
21
- exportScoreConfigs, importScoreConfigs,
22
- applyScoreConfig,
23
- getBuiltInPresets, installPreset,
24
- formatScoresTable, formatScoreConfigsTable,
25
- } from './scores.js';
26
- export type { ScoreFilters } from './scores.js';
27
-
28
- export { listComments, getComment, createComment } from './comments.js';
29
- export type { CommentFilters } from './comments.js';
30
-
31
- export {
32
- uploadAndAttachMedia, getMedia, detectContentType, formatMediaDisplay,
33
- } from './media.js';
@@ -1,193 +0,0 @@
1
- // coaiajs/src/langfuse/media.ts — Media upload operations
2
- // Port of cofuse.py media functions
3
-
4
- import { readFileSync, statSync } from 'node:fs';
5
- import { createHash } from 'node:crypto';
6
- import { basename, extname } from 'node:path';
7
- import { getClient } from './client.js';
8
-
9
- const SUPPORTED_CONTENT_TYPES = new Set([
10
- 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
11
- 'image/tiff', 'image/bmp', 'image/avif',
12
- 'audio/mpeg', 'audio/mp4', 'audio/wav', 'audio/ogg', 'audio/webm', 'audio/flac',
13
- 'video/mp4', 'video/webm', 'video/ogg', 'video/quicktime',
14
- 'application/pdf',
15
- 'text/plain', 'text/html', 'text/css', 'text/csv',
16
- 'application/json', 'application/xml',
17
- ]);
18
-
19
- const EXTENSION_MAP: Record<string, string> = {
20
- '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png',
21
- '.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml',
22
- '.tiff': 'image/tiff', '.tif': 'image/tiff', '.bmp': 'image/bmp',
23
- '.avif': 'image/avif',
24
- '.mp3': 'audio/mpeg', '.m4a': 'audio/mp4', '.wav': 'audio/wav',
25
- '.ogg': 'audio/ogg', '.flac': 'audio/flac', '.weba': 'audio/webm',
26
- '.mp4': 'video/mp4', '.webm': 'video/webm', '.ogv': 'video/ogg',
27
- '.mov': 'video/quicktime',
28
- '.pdf': 'application/pdf',
29
- '.txt': 'text/plain', '.html': 'text/html', '.htm': 'text/html',
30
- '.css': 'text/css', '.csv': 'text/csv',
31
- '.json': 'application/json', '.xml': 'application/xml',
32
- };
33
-
34
- const TRUSTED_DOMAINS = [
35
- 'amazonaws.com',
36
- 's3.amazonaws.com',
37
- 'storage.googleapis.com',
38
- 'blob.core.windows.net',
39
- 'r2.cloudflarestorage.com',
40
- ];
41
-
42
- export async function uploadAndAttachMedia(params: {
43
- filePath: string;
44
- traceId: string;
45
- field?: string;
46
- observationId?: string;
47
- contentType?: string;
48
- }): Promise<string> {
49
- const { filePath, traceId, observationId } = params;
50
- const field = params.field ?? 'input';
51
-
52
- // Detect content type
53
- const contentType = params.contentType ?? detectContentType(filePath);
54
- if (!SUPPORTED_CONTENT_TYPES.has(contentType)) {
55
- return JSON.stringify({ error: `Unsupported content type: ${contentType}` });
56
- }
57
-
58
- // Calculate file info
59
- const stat = statSync(filePath);
60
- const fileData = readFileSync(filePath);
61
- const sha256 = createHash('sha256').update(fileData).digest('base64');
62
-
63
- const client = getClient();
64
-
65
- // Step 1: Get presigned upload URL
66
- const uploadReqData: Record<string, unknown> = {
67
- traceId,
68
- contentType,
69
- contentLength: stat.size,
70
- sha256Hash: sha256,
71
- field,
72
- };
73
- if (observationId) uploadReqData.observationId = observationId;
74
-
75
- const uploadInfo = await client.request<Record<string, unknown>>(
76
- 'POST',
77
- '/api/public/media',
78
- uploadReqData,
79
- );
80
-
81
- const uploadUrl = uploadInfo.uploadUrl as string;
82
- const mediaId = uploadInfo.mediaId as string;
83
-
84
- if (!uploadUrl || !mediaId) {
85
- return JSON.stringify({ error: 'Failed to get upload URL', detail: uploadInfo });
86
- }
87
-
88
- // Step 2: Validate upload URL domain
89
- const url = new URL(uploadUrl);
90
- if (!isTrustedDomain(url.hostname)) {
91
- return JSON.stringify({
92
- error: `Security: Upload URL domain '${url.hostname}' is not trusted`,
93
- });
94
- }
95
-
96
- // Step 3: Upload to presigned URL
97
- const startTime = Date.now();
98
- const uploadResponse = await fetch(uploadUrl, {
99
- method: 'PUT',
100
- headers: {
101
- 'Content-Type': contentType,
102
- 'x-amz-checksum-sha256': sha256,
103
- },
104
- body: fileData,
105
- });
106
- const uploadTimeMs = Date.now() - startTime;
107
-
108
- // Step 4: Patch status
109
- await client.request('PATCH', `/api/public/media/${mediaId}`, {
110
- uploadHttpStatus: uploadResponse.status,
111
- uploadTimeMs,
112
- uploadedAt: new Date().toISOString(),
113
- });
114
-
115
- if (!uploadResponse.ok) {
116
- return JSON.stringify({
117
- error: `Upload failed: ${uploadResponse.status}`,
118
- mediaId,
119
- });
120
- }
121
-
122
- // Build media token
123
- const token = `@@@langfuseMedia:type=${contentType}|id=${mediaId}|source=file@@@`;
124
-
125
- return JSON.stringify({
126
- success: true,
127
- mediaId,
128
- fileName: basename(filePath),
129
- contentType,
130
- contentLength: stat.size,
131
- traceId,
132
- field,
133
- observationId: observationId ?? null,
134
- uploadTimeMs,
135
- mediaToken: token,
136
- }, null, 2);
137
- }
138
-
139
- export async function getMedia(mediaId: string): Promise<string> {
140
- const client = getClient();
141
- const result = await client.request<unknown>('GET', `/api/public/media/${mediaId}`);
142
- return JSON.stringify(result, null, 2);
143
- }
144
-
145
- export function detectContentType(filePath: string): string {
146
- const ext = extname(filePath).toLowerCase();
147
- return EXTENSION_MAP[ext] ?? 'application/octet-stream';
148
- }
149
-
150
- export function formatMediaDisplay(json: unknown): string {
151
- try {
152
- const media = (typeof json === 'string' ? JSON.parse(json) : json) as Record<string, unknown>;
153
- if ('error' in media) return `Error: ${media.error}`;
154
-
155
- const contentType = String(media.contentType ?? 'unknown');
156
- let glyph = '📎';
157
- if (contentType.startsWith('image/')) glyph = '🖼️';
158
- else if (contentType.startsWith('video/')) glyph = '🎥';
159
- else if (contentType.startsWith('audio/')) glyph = '🎵';
160
- else if (contentType === 'application/pdf') glyph = '📄';
161
-
162
- const lines: string[] = [];
163
- lines.push(`${glyph} Media: ${media.fileName ?? 'Unnamed'}`);
164
- lines.push(`├── 🆔 ID: ${media.id ?? media.mediaId ?? 'N/A'}`);
165
- lines.push(`├── 📝 Content Type: ${contentType}`);
166
- lines.push(`├── 📏 Size: ${media.contentLength ?? 0} bytes`);
167
- lines.push(`├── 🔗 Trace ID: ${media.traceId ?? 'N/A'}`);
168
-
169
- if (media.observationId) lines.push(`├── 👁️ Observation ID: ${media.observationId}`);
170
- if (media.field) lines.push(`├── 🏷️ Field: ${media.field}`);
171
- if (media.uploadedAt) lines.push(`├── ⏰ Uploaded: ${String(media.uploadedAt).slice(0, 19)}`);
172
-
173
- // Fix last line to └──
174
- if (lines.length > 1) {
175
- const last = lines[lines.length - 1];
176
- lines[lines.length - 1] = last.replace('├── ', '└── ');
177
- }
178
-
179
- return lines.join('\n');
180
- } catch (e) {
181
- return `Error formatting media: ${e}`;
182
- }
183
- }
184
-
185
- // ─── Helpers ────────────────────────────────────────────────────────
186
-
187
- function isTrustedDomain(hostname: string): boolean {
188
- const lower = hostname.toLowerCase();
189
- for (const domain of TRUSTED_DOMAINS) {
190
- if (lower === domain || lower.endsWith(`.${domain}`)) return true;
191
- }
192
- return false;
193
- }
@@ -1,131 +0,0 @@
1
- // coaiajs/src/langfuse/observations.ts — Observation operations
2
- // Port of cofuse.py observation functions
3
-
4
- import { v4 as uuidv4 } from 'uuid';
5
- import { getClient, nowISO } from './client.js';
6
- import type { IngestionEvent } from './client.js';
7
-
8
- export async function addObservation(params: {
9
- observationId?: string;
10
- traceId: string;
11
- type?: string;
12
- name: string;
13
- parentId?: string;
14
- inputData?: unknown;
15
- outputData?: unknown;
16
- metadata?: unknown;
17
- startTime?: string;
18
- endTime?: string;
19
- level?: string;
20
- model?: string;
21
- usage?: unknown;
22
- }): Promise<string> {
23
- const client = getClient();
24
- const now = nowISO();
25
- const obsId = params.observationId ?? uuidv4();
26
-
27
- const body: Record<string, unknown> = {
28
- id: obsId,
29
- traceId: params.traceId,
30
- type: params.type ?? 'EVENT',
31
- startTime: params.startTime ?? now,
32
- level: params.level ?? 'DEFAULT',
33
- };
34
-
35
- if (params.name) body.name = params.name;
36
- if (params.inputData) body.input = params.inputData;
37
- if (params.outputData) body.output = params.outputData;
38
- if (params.metadata) body.metadata = params.metadata;
39
- if (params.parentId) body.parentObservationId = params.parentId;
40
- if (params.endTime) body.endTime = params.endTime;
41
- if (params.model) body.model = params.model;
42
- if (params.usage) body.usage = params.usage;
43
-
44
- const event: IngestionEvent = {
45
- id: `${obsId}-event`,
46
- timestamp: now,
47
- type: 'observation-create',
48
- body,
49
- };
50
-
51
- await client.ingest([event]);
52
-
53
- return JSON.stringify({
54
- success: true,
55
- observationId: obsId,
56
- traceId: params.traceId,
57
- name: params.name,
58
- type: params.type ?? 'EVENT',
59
- }, null, 2);
60
- }
61
-
62
- export async function getObservation(observationId: string): Promise<string> {
63
- const client = getClient();
64
- const result = await client.request<unknown>('GET', `/api/public/observations/${observationId}`);
65
- return JSON.stringify(result, null, 2);
66
- }
67
-
68
- // ─── Formatters ─────────────────────────────────────────────────────
69
-
70
- const TYPE_GLYPHS: Record<string, string> = {
71
- SPAN: '🔗',
72
- GENERATION: '🤖',
73
- EVENT: '⚡',
74
- DEFAULT: '📦',
75
- };
76
-
77
- export function formatObservationDisplay(json: unknown): string {
78
- try {
79
- const obs = (typeof json === 'string' ? JSON.parse(json) : json) as Record<string, unknown>;
80
- if ('error' in obs) return `Error: ${obs.error}`;
81
-
82
- const obsType = String(obs.type ?? 'UNKNOWN').toUpperCase();
83
- const glyph = TYPE_GLYPHS[obsType] ?? TYPE_GLYPHS.DEFAULT;
84
-
85
- const lines: string[] = [];
86
- lines.push(`${glyph} Observation: ${obs.name ?? 'Unnamed'}`);
87
- lines.push(`├── 🆔 ID: ${obs.id ?? 'N/A'}`);
88
- lines.push(`├── 📋 Type: ${obsType}`);
89
- lines.push(`├── 🔗 Trace: ${obs.traceId ?? 'N/A'}`);
90
- lines.push(`├── ⏰ Start: ${String(obs.startTime ?? 'N/A').slice(0, 19)}`);
91
-
92
- if (obs.endTime) lines.push(`├── ⏰ End: ${String(obs.endTime).slice(0, 19)}`);
93
- if (obs.level && obs.level !== 'DEFAULT') lines.push(`├── 📊 Level: ${obs.level}`);
94
- if (obs.model) lines.push(`├── 🤖 Model: ${obs.model}`);
95
-
96
- if (obs.parentObservationId) {
97
- lines.push(`├── 👆 Parent: ${obs.parentObservationId}`);
98
- }
99
-
100
- if (obs.input) {
101
- const txt = String(obs.input).replace(/\n/g, ' ');
102
- lines.push(`├── 📥 Input: ${txt.length > 100 ? txt.slice(0, 100) + '...' : txt}`);
103
- }
104
- if (obs.output) {
105
- const txt = String(obs.output).replace(/\n/g, ' ');
106
- lines.push(`├── 📤 Output: ${txt.length > 100 ? txt.slice(0, 100) + '...' : txt}`);
107
- }
108
-
109
- const metadata = obs.metadata as Record<string, unknown> | undefined;
110
- if (metadata && typeof metadata === 'object' && Object.keys(metadata).length) {
111
- lines.push(`└── 📋 Metadata:`);
112
- const entries = Object.entries(metadata);
113
- entries.forEach(([k, v], i) => {
114
- const pre = i === entries.length - 1 ? '└── ' : '├── ';
115
- lines.push(` ${pre}${k}: ${JSON.stringify(v)}`);
116
- });
117
- } else {
118
- // Fix last ├── to └──
119
- if (lines.length > 0) {
120
- const last = lines[lines.length - 1];
121
- if (last.startsWith('├── ')) {
122
- lines[lines.length - 1] = last.replace('├── ', '└── ');
123
- }
124
- }
125
- }
126
-
127
- return lines.join('\n');
128
- } catch (e) {
129
- return `Error formatting observation: ${e}`;
130
- }
131
- }
@@ -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
- }