coaiajs 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +16 -0
- package/README.md +2 -0
- package/articles/academic/NOTES.md +1 -0
- package/articles/academic/README.md +1 -0
- package/articles/academic/creative-orientation-vs-problem-solving.md +177 -0
- package/articles/academic/jsonl-knowledge-graphs-agent-memory.md +142 -0
- package/articles/academic/langfuse-observability-llm-pipelines.md +144 -0
- package/articles/academic/medicine-wheel-software-architecture.md +163 -0
- package/articles/academic/mmot-autonomous-agents.md +156 -0
- package/articles/academic/model-context-protocol-interagent.md +161 -0
- package/articles/academic/pde-prompt-decomposition.md +186 -0
- package/articles/academic/structural-tension-in-ai-agents.md +134 -0
- package/articles/reviews/mcp-protocol-design-review.md +170 -0
- package/articles/reviews/observability-ai-systems-review.md +176 -0
- package/articles/reviews/prompt-engineering-decomposition-review.md +184 -0
- package/articles/surveys/agent-orchestration-survey.md +186 -0
- package/articles/surveys/knowledge-graph-storage-survey.md +204 -0
- package/articles/surveys/structural-tension-methodology-survey.md +154 -0
- package/articles/technical/aws-sdk-v3-polly.md +270 -0
- package/articles/technical/commander-cli-framework.md +262 -0
- package/articles/technical/dotenv-config-patterns.md +360 -0
- package/articles/technical/ioredis-vs-redis.md +142 -0
- package/articles/technical/langfuse-js-sdk-vs-rest.md +191 -0
- package/articles/technical/mcp-sdk-typescript.md +291 -0
- package/articles/technical/octokit-github-api.md +293 -0
- package/articles/technical/openai-sdk-modern.md +231 -0
- package/articles/technical/yaml-parsing-node.md +266 -0
- package/articles/technical/zod-runtime-validation.md +212 -0
- package/dist/mcp/server.js +5 -3
- package/dist/mcp/tools/coaiapy-tools.js +1 -0
- package/dist/src/cli.js +41 -5
- package/dist/src/langfuse/index.d.ts +1 -1
- package/dist/src/langfuse/index.js +1 -1
- package/dist/src/langfuse/traces.d.ts +1 -0
- package/dist/src/langfuse/traces.js +24 -0
- package/dist/src/redis.d.ts +1 -1
- package/dist/src/redis.js +2 -2
- package/mcp/config.ts +225 -0
- package/mcp/prompts.ts +131 -0
- package/mcp/resources.ts +84 -0
- package/mcp/server.ts +518 -0
- package/mcp/tools/coaiapy-tools.ts +366 -0
- package/mcp/tools/index.ts +4 -0
- package/package.json +2 -67
- package/src/audio.ts +76 -0
- package/src/cli-helpers.ts +86 -0
- package/src/cli.ts +1260 -0
- package/src/config.ts +207 -0
- package/src/environment.ts +171 -0
- package/src/github.ts +143 -0
- package/src/index.ts +82 -0
- package/src/langfuse/client.ts +105 -0
- package/src/langfuse/comments.ts +52 -0
- package/src/langfuse/datasets.ts +178 -0
- package/src/langfuse/index.ts +33 -0
- package/src/langfuse/media.ts +193 -0
- package/src/langfuse/observations.ts +131 -0
- package/src/langfuse/prompts.ts +157 -0
- package/src/langfuse/scores.ts +456 -0
- package/src/langfuse/traces.ts +302 -0
- package/src/llm.ts +106 -0
- package/src/narrative/graph-manager.ts +1358 -0
- package/src/narrative/index.ts +188 -0
- package/src/narrative/markdown-export.ts +535 -0
- package/src/narrative/tool-definitions.ts +635 -0
- package/src/narrative/tool-handlers.ts +528 -0
- package/src/narrative/types.ts +9 -0
- package/src/narrative/validation.ts +179 -0
- package/src/pde/index.ts +34 -0
- package/src/pde/mcp-handlers.ts +359 -0
- package/src/pde/mcp-tools.ts +201 -0
- package/src/pde/session-manager.ts +248 -0
- package/src/pde/stc-mapper.ts +298 -0
- package/src/pipeline/index.ts +7 -0
- package/src/pipeline/template-engine.ts +398 -0
- package/src/planning/index.ts +32 -0
- package/src/planning/mcp-handlers.ts +369 -0
- package/src/planning/mcp-tools.ts +155 -0
- package/src/planning/plan-parser.ts +587 -0
- package/src/redis.ts +111 -0
- package/src/types.ts +281 -0
- package/test/config.test.mjs +93 -0
- package/tsconfig.json +26 -0
- package/dist/mcp/config.d.ts.map +0 -1
- package/dist/mcp/config.js.map +0 -1
- package/dist/mcp/prompts.d.ts.map +0 -1
- package/dist/mcp/prompts.js.map +0 -1
- package/dist/mcp/resources.d.ts.map +0 -1
- package/dist/mcp/resources.js.map +0 -1
- package/dist/mcp/server.d.ts.map +0 -1
- package/dist/mcp/server.js.map +0 -1
- package/dist/mcp/tools/coaiapy-tools.d.ts.map +0 -1
- package/dist/mcp/tools/coaiapy-tools.js.map +0 -1
- package/dist/mcp/tools/index.d.ts.map +0 -1
- package/dist/mcp/tools/index.js.map +0 -1
- package/dist/src/audio.d.ts.map +0 -1
- package/dist/src/audio.js.map +0 -1
- package/dist/src/cli-helpers.d.ts.map +0 -1
- package/dist/src/cli-helpers.js.map +0 -1
- package/dist/src/cli.d.ts.map +0 -1
- package/dist/src/cli.js.map +0 -1
- package/dist/src/config.d.ts.map +0 -1
- package/dist/src/config.js.map +0 -1
- package/dist/src/environment.d.ts.map +0 -1
- package/dist/src/environment.js.map +0 -1
- package/dist/src/github.d.ts.map +0 -1
- package/dist/src/github.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/langfuse/client.d.ts.map +0 -1
- package/dist/src/langfuse/client.js.map +0 -1
- package/dist/src/langfuse/comments.d.ts.map +0 -1
- package/dist/src/langfuse/comments.js.map +0 -1
- package/dist/src/langfuse/datasets.d.ts.map +0 -1
- package/dist/src/langfuse/datasets.js.map +0 -1
- package/dist/src/langfuse/index.d.ts.map +0 -1
- package/dist/src/langfuse/index.js.map +0 -1
- package/dist/src/langfuse/media.d.ts.map +0 -1
- package/dist/src/langfuse/media.js.map +0 -1
- package/dist/src/langfuse/observations.d.ts.map +0 -1
- package/dist/src/langfuse/observations.js.map +0 -1
- package/dist/src/langfuse/prompts.d.ts.map +0 -1
- package/dist/src/langfuse/prompts.js.map +0 -1
- package/dist/src/langfuse/scores.d.ts.map +0 -1
- package/dist/src/langfuse/scores.js.map +0 -1
- package/dist/src/langfuse/traces.d.ts.map +0 -1
- package/dist/src/langfuse/traces.js.map +0 -1
- package/dist/src/llm.d.ts.map +0 -1
- package/dist/src/llm.js.map +0 -1
- package/dist/src/narrative/graph-manager.d.ts.map +0 -1
- package/dist/src/narrative/graph-manager.js.map +0 -1
- package/dist/src/narrative/index.d.ts.map +0 -1
- package/dist/src/narrative/index.js.map +0 -1
- package/dist/src/narrative/markdown-export.d.ts.map +0 -1
- package/dist/src/narrative/markdown-export.js.map +0 -1
- package/dist/src/narrative/tool-definitions.d.ts.map +0 -1
- package/dist/src/narrative/tool-definitions.js.map +0 -1
- package/dist/src/narrative/tool-handlers.d.ts.map +0 -1
- package/dist/src/narrative/tool-handlers.js.map +0 -1
- package/dist/src/narrative/types.d.ts.map +0 -1
- package/dist/src/narrative/types.js.map +0 -1
- package/dist/src/narrative/validation.d.ts.map +0 -1
- package/dist/src/narrative/validation.js.map +0 -1
- package/dist/src/pde/index.d.ts.map +0 -1
- package/dist/src/pde/index.js.map +0 -1
- package/dist/src/pde/mcp-handlers.d.ts.map +0 -1
- package/dist/src/pde/mcp-handlers.js.map +0 -1
- package/dist/src/pde/mcp-tools.d.ts.map +0 -1
- package/dist/src/pde/mcp-tools.js.map +0 -1
- package/dist/src/pde/session-manager.d.ts.map +0 -1
- package/dist/src/pde/session-manager.js.map +0 -1
- package/dist/src/pde/stc-mapper.d.ts.map +0 -1
- package/dist/src/pde/stc-mapper.js.map +0 -1
- package/dist/src/pipeline/index.d.ts.map +0 -1
- package/dist/src/pipeline/index.js.map +0 -1
- package/dist/src/pipeline/template-engine.d.ts.map +0 -1
- package/dist/src/pipeline/template-engine.js.map +0 -1
- package/dist/src/planning/index.d.ts.map +0 -1
- package/dist/src/planning/index.js.map +0 -1
- package/dist/src/planning/mcp-handlers.d.ts.map +0 -1
- package/dist/src/planning/mcp-handlers.js.map +0 -1
- package/dist/src/planning/mcp-tools.d.ts.map +0 -1
- package/dist/src/planning/mcp-tools.js.map +0 -1
- package/dist/src/planning/plan-parser.d.ts.map +0 -1
- package/dist/src/planning/plan-parser.js.map +0 -1
- package/dist/src/redis.d.ts.map +0 -1
- package/dist/src/redis.js.map +0 -1
- package/dist/src/types.d.ts.map +0 -1
- package/dist/src/types.js.map +0 -1
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
# @octokit/rest: Technical Assessment for CoAiA.js
|
|
2
|
+
|
|
3
|
+
> Package selection brief — GitHub REST API client replacing coaiapy's raw requests in cogh.py
|
|
4
|
+
|
|
5
|
+
## Summary & Recommendation
|
|
6
|
+
|
|
7
|
+
**Use `@octokit/rest` v22.x** (currently 22.0.1). Octokit is the official GitHub-maintained JavaScript client for the GitHub REST API. It replaces coaiapy's `cogh.py` which manually constructs HTTP requests with auth headers. Octokit provides typed methods for every GitHub endpoint, automatic pagination, built-in authentication, and rate limit handling.
|
|
8
|
+
|
|
9
|
+
**Pin:** `"@octokit/rest": "^22.0.0"`
|
|
10
|
+
|
|
11
|
+
## What We're Replacing
|
|
12
|
+
|
|
13
|
+
Coaiapy's `cogh.py` uses raw HTTP requests:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# coaiapy/cogh.py — manual GitHub API
|
|
17
|
+
import requests
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
def _get_github_headers():
|
|
21
|
+
config = read_config()
|
|
22
|
+
token = config.get("github", {}).get("api_token") or os.getenv("GH_TOKEN")
|
|
23
|
+
return {
|
|
24
|
+
"Authorization": f"Bearer {token}",
|
|
25
|
+
"Accept": "application/vnd.github.v3+json"
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
def list_issues(owner, repo):
|
|
29
|
+
headers = _get_github_headers()
|
|
30
|
+
response = requests.get(
|
|
31
|
+
f"https://api.github.com/repos/{owner}/{repo}/issues",
|
|
32
|
+
headers=headers
|
|
33
|
+
)
|
|
34
|
+
return response.json()
|
|
35
|
+
|
|
36
|
+
def get_issue(owner, repo, issue_number):
|
|
37
|
+
headers = _get_github_headers()
|
|
38
|
+
response = requests.get(
|
|
39
|
+
f"https://api.github.com/repos/{owner}/{repo}/issues/{issue_number}",
|
|
40
|
+
headers=headers
|
|
41
|
+
)
|
|
42
|
+
return response.json()
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Issues: no pagination handling, no rate limit awareness, no type safety, manual URL construction, no retry logic.
|
|
46
|
+
|
|
47
|
+
## Options Compared
|
|
48
|
+
|
|
49
|
+
| Feature | @octokit/rest v22 | Raw fetch/requests | graphql (@octokit/graphql) |
|
|
50
|
+
|---------|-------------------|-------------------|--------------------------|
|
|
51
|
+
| API coverage | All REST endpoints typed | Manual per endpoint | GraphQL (different paradigm) |
|
|
52
|
+
| Authentication | Token, App, OAuth, Action | Manual headers | Same as octokit |
|
|
53
|
+
| Pagination | `octokit.paginate()` automatic | Manual `Link` header parsing | Cursor-based (manual) |
|
|
54
|
+
| Rate limiting | Built-in retry plugin | Manual `X-RateLimit-*` handling | Same as octokit |
|
|
55
|
+
| TypeScript | Full types for every endpoint | Manual interfaces | Partial |
|
|
56
|
+
| Request/response hooks | Plugin system | Manual interceptors | Plugin system |
|
|
57
|
+
| Bundle size | ~150KB | 0 | ~50KB |
|
|
58
|
+
| Maintenance | GitHub-maintained official | N/A | GitHub-maintained official |
|
|
59
|
+
|
|
60
|
+
## API Overview
|
|
61
|
+
|
|
62
|
+
### Client Setup
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { Octokit } from '@octokit/rest';
|
|
66
|
+
import { loadConfig } from '../config.js';
|
|
67
|
+
|
|
68
|
+
const config = loadConfig();
|
|
69
|
+
|
|
70
|
+
const octokit = new Octokit({
|
|
71
|
+
auth: config.githubToken ?? process.env.GH_TOKEN,
|
|
72
|
+
userAgent: 'coaiajs/1.0.0',
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Issues API (replacing cogh.py)
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// List issues (replaces cogh.list_issues)
|
|
80
|
+
async function listIssues(owner: string, repo: string, options?: {
|
|
81
|
+
state?: 'open' | 'closed' | 'all';
|
|
82
|
+
labels?: string[];
|
|
83
|
+
per_page?: number;
|
|
84
|
+
}) {
|
|
85
|
+
const { data } = await octokit.rest.issues.listForRepo({
|
|
86
|
+
owner,
|
|
87
|
+
repo,
|
|
88
|
+
state: options?.state ?? 'open',
|
|
89
|
+
labels: options?.labels?.join(','),
|
|
90
|
+
per_page: options?.per_page ?? 30,
|
|
91
|
+
});
|
|
92
|
+
return data;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Get single issue (replaces cogh.get_issue)
|
|
96
|
+
async function getIssue(owner: string, repo: string, issueNumber: number) {
|
|
97
|
+
const { data } = await octokit.rest.issues.get({
|
|
98
|
+
owner,
|
|
99
|
+
repo,
|
|
100
|
+
issue_number: issueNumber,
|
|
101
|
+
});
|
|
102
|
+
return data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Create issue
|
|
106
|
+
async function createIssue(owner: string, repo: string, title: string, body: string, labels?: string[]) {
|
|
107
|
+
const { data } = await octokit.rest.issues.create({
|
|
108
|
+
owner,
|
|
109
|
+
repo,
|
|
110
|
+
title,
|
|
111
|
+
body,
|
|
112
|
+
labels,
|
|
113
|
+
});
|
|
114
|
+
return data;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Add comment to issue
|
|
118
|
+
async function commentOnIssue(owner: string, repo: string, issueNumber: number, body: string) {
|
|
119
|
+
const { data } = await octokit.rest.issues.createComment({
|
|
120
|
+
owner,
|
|
121
|
+
repo,
|
|
122
|
+
issue_number: issueNumber,
|
|
123
|
+
body,
|
|
124
|
+
});
|
|
125
|
+
return data;
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Automatic Pagination
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
// Fetch ALL issues across all pages
|
|
133
|
+
async function listAllIssues(owner: string, repo: string) {
|
|
134
|
+
const issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
|
135
|
+
owner,
|
|
136
|
+
repo,
|
|
137
|
+
state: 'all',
|
|
138
|
+
per_page: 100,
|
|
139
|
+
});
|
|
140
|
+
return issues; // All pages concatenated
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Session Management (Miadi webhook pattern)
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// Create issue for agent session tracking
|
|
148
|
+
async function createSessionIssue(
|
|
149
|
+
owner: string,
|
|
150
|
+
repo: string,
|
|
151
|
+
sessionId: string,
|
|
152
|
+
metadata: Record<string, string>
|
|
153
|
+
) {
|
|
154
|
+
const body = [
|
|
155
|
+
`## Agent Session: ${sessionId}`,
|
|
156
|
+
'',
|
|
157
|
+
'| Key | Value |',
|
|
158
|
+
'|-----|-------|',
|
|
159
|
+
...Object.entries(metadata).map(([k, v]) => `| ${k} | ${v} |`),
|
|
160
|
+
].join('\n');
|
|
161
|
+
|
|
162
|
+
return createIssue(owner, repo, `Session: ${sessionId}`, body, ['agent-session']);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Update session with results
|
|
166
|
+
async function updateSessionIssue(
|
|
167
|
+
owner: string,
|
|
168
|
+
repo: string,
|
|
169
|
+
issueNumber: number,
|
|
170
|
+
results: string
|
|
171
|
+
) {
|
|
172
|
+
await commentOnIssue(owner, repo, issueNumber, results);
|
|
173
|
+
|
|
174
|
+
// Close when session completes
|
|
175
|
+
await octokit.rest.issues.update({
|
|
176
|
+
owner,
|
|
177
|
+
repo,
|
|
178
|
+
issue_number: issueNumber,
|
|
179
|
+
state: 'closed',
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Repository Operations
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
// Get file contents
|
|
188
|
+
async function getFileContent(owner: string, repo: string, path: string, ref?: string) {
|
|
189
|
+
const { data } = await octokit.rest.repos.getContent({
|
|
190
|
+
owner,
|
|
191
|
+
repo,
|
|
192
|
+
path,
|
|
193
|
+
ref,
|
|
194
|
+
});
|
|
195
|
+
if ('content' in data) {
|
|
196
|
+
return Buffer.from(data.content, 'base64').toString('utf-8');
|
|
197
|
+
}
|
|
198
|
+
throw new Error(`${path} is not a file`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// List commits
|
|
202
|
+
async function listRecentCommits(owner: string, repo: string, count = 10) {
|
|
203
|
+
const { data } = await octokit.rest.repos.listCommits({
|
|
204
|
+
owner,
|
|
205
|
+
repo,
|
|
206
|
+
per_page: count,
|
|
207
|
+
});
|
|
208
|
+
return data.map(c => ({
|
|
209
|
+
sha: c.sha.slice(0, 7),
|
|
210
|
+
message: c.commit.message.split('\n')[0],
|
|
211
|
+
author: c.commit.author?.name,
|
|
212
|
+
date: c.commit.author?.date,
|
|
213
|
+
}));
|
|
214
|
+
}
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Authentication Patterns
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Pattern 1: Personal Access Token (default)
|
|
221
|
+
const octokit = new Octokit({ auth: process.env.GH_TOKEN });
|
|
222
|
+
|
|
223
|
+
// Pattern 2: GitHub Actions token
|
|
224
|
+
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN });
|
|
225
|
+
|
|
226
|
+
// Pattern 3: GitHub App (for Miadi webhook integration)
|
|
227
|
+
import { createAppAuth } from '@octokit/auth-app';
|
|
228
|
+
|
|
229
|
+
const octokit = new Octokit({
|
|
230
|
+
authStrategy: createAppAuth,
|
|
231
|
+
auth: {
|
|
232
|
+
appId: config.githubAppId,
|
|
233
|
+
privateKey: config.githubPrivateKey,
|
|
234
|
+
installationId: config.githubInstallationId,
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Formatting (replacing cogh.format_issues_table)
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
function formatIssuesTable(issues: Awaited<ReturnType<typeof listIssues>>): string {
|
|
243
|
+
const header = '| # | Title | State | Labels | Updated |';
|
|
244
|
+
const separator = '|---|-------|-------|--------|---------|';
|
|
245
|
+
const rows = issues.map(i =>
|
|
246
|
+
`| ${i.number} | ${i.title} | ${i.state} | ${i.labels.map(l =>
|
|
247
|
+
typeof l === 'string' ? l : l.name).join(', ')} | ${i.updated_at?.slice(0, 10)} |`
|
|
248
|
+
);
|
|
249
|
+
return [header, separator, ...rows].join('\n');
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Integration Plan
|
|
254
|
+
|
|
255
|
+
1. **Core client:** `src/github/client.ts` — Octokit singleton from config
|
|
256
|
+
2. **Issues module:** `src/github/issues.ts` — CRUD + pagination (replaces cogh.py)
|
|
257
|
+
3. **Session tracking:** `src/github/sessions.ts` — issue-based session management
|
|
258
|
+
4. **Repository module:** `src/github/repos.ts` — file content, commits
|
|
259
|
+
5. **CLI command:** `src/commands/gh.ts` — Commander subcommand for GitHub ops
|
|
260
|
+
6. **MCP tool:** `mcp/tools/github.ts` — GitHub operations as agent tools
|
|
261
|
+
7. **Auth:** Support PAT, Actions token, and GitHub App auth
|
|
262
|
+
|
|
263
|
+
## Version & Ecosystem
|
|
264
|
+
|
|
265
|
+
| Metric | Value |
|
|
266
|
+
|--------|-------|
|
|
267
|
+
| Current version | 22.0.1 (Feb 2026) |
|
|
268
|
+
| Weekly downloads | ~10M+ |
|
|
269
|
+
| TypeScript | Full native types for every endpoint |
|
|
270
|
+
| Node.js compat | ≥20 (v22 dropped Node 18) |
|
|
271
|
+
| Authentication | PAT, OAuth, App, Actions |
|
|
272
|
+
| Pagination | Built-in `octokit.paginate()` |
|
|
273
|
+
| Rate limiting | Plugin-based retry |
|
|
274
|
+
| Maintained by | GitHub (official) |
|
|
275
|
+
| License | MIT |
|
|
276
|
+
|
|
277
|
+
### Ecosystem Packages
|
|
278
|
+
|
|
279
|
+
| Package | Purpose | When to Use |
|
|
280
|
+
|---------|---------|-------------|
|
|
281
|
+
| `@octokit/rest` | REST API client | Primary — all REST operations |
|
|
282
|
+
| `@octokit/graphql` | GraphQL API | Complex queries (PR reviews, nested data) |
|
|
283
|
+
| `@octokit/auth-app` | GitHub App auth | Miadi webhook integration |
|
|
284
|
+
| `@octokit/webhooks` | Webhook handling | Receiving GitHub webhooks |
|
|
285
|
+
| `@octokit/plugin-paginate-rest` | Pagination | Included in @octokit/rest |
|
|
286
|
+
|
|
287
|
+
## References
|
|
288
|
+
|
|
289
|
+
- npm: https://www.npmjs.com/package/@octokit/rest
|
|
290
|
+
- GitHub: https://github.com/octokit/rest.js
|
|
291
|
+
- Docs: https://octokit.github.io/rest.js/v22/
|
|
292
|
+
- Auth patterns: https://github.com/octokit/auth-app.js
|
|
293
|
+
- Releases: https://github.com/octokit/rest.js/releases
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# OpenAI Node.js SDK: Technical Assessment for CoAiA.js
|
|
2
|
+
|
|
3
|
+
> Package selection brief — Unified LLM client for chat completions, streaming, and Whisper transcription replacing coaiapy's raw HTTP calls
|
|
4
|
+
|
|
5
|
+
## Summary & Recommendation
|
|
6
|
+
|
|
7
|
+
**Use `openai` v6.x SDK** (currently 6.27.0). The official SDK provides type-safe chat completions with streaming, Whisper audio transcription, and image generation — all features coaiajs needs. The SDK handles authentication, retry logic, rate limiting, and model-specific serialization. Combined with `@langfuse/openai` for automatic tracing, this eliminates all raw HTTP request code.
|
|
8
|
+
|
|
9
|
+
**Pin:** `"openai": "^6.0.0"`
|
|
10
|
+
|
|
11
|
+
## What We're Replacing
|
|
12
|
+
|
|
13
|
+
Coaiapy uses raw HTTP requests for OpenAI API calls, scattered across multiple modules:
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
# coaiapy pattern — manual HTTP to OpenAI
|
|
17
|
+
import requests
|
|
18
|
+
import json
|
|
19
|
+
|
|
20
|
+
def call_openai(messages, model="gpt-4"):
|
|
21
|
+
headers = {
|
|
22
|
+
"Authorization": f"Bearer {os.getenv('OPENAI_API_KEY')}",
|
|
23
|
+
"Content-Type": "application/json"
|
|
24
|
+
}
|
|
25
|
+
response = requests.post(
|
|
26
|
+
"https://api.openai.com/v1/chat/completions",
|
|
27
|
+
headers=headers,
|
|
28
|
+
json={"model": model, "messages": messages}
|
|
29
|
+
)
|
|
30
|
+
return response.json()["choices"][0]["message"]["content"]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This pattern: no streaming, no automatic retry, no type safety, no token counting, no multimodal support, and requires manual header management per request.
|
|
34
|
+
|
|
35
|
+
## Options Compared
|
|
36
|
+
|
|
37
|
+
| Feature | openai SDK v6 | Raw fetch/requests | LangChain OpenAI |
|
|
38
|
+
|---------|--------------|-------------------|------------------|
|
|
39
|
+
| Type safety | Full TypeScript types | Manual interfaces | Via LangChain types |
|
|
40
|
+
| Streaming | `stream: true` + async iterator | Manual SSE parsing | LangChain callbacks |
|
|
41
|
+
| Whisper transcription | `client.audio.transcriptions.create()` | Manual multipart form | Not supported |
|
|
42
|
+
| Image generation | `client.images.generate()` | Manual API call | Via LangChain |
|
|
43
|
+
| Rate limit handling | Built-in retry with backoff | Manual retry loops | Via LangChain |
|
|
44
|
+
| Token counting | Response includes usage | Manual counting | Via callbacks |
|
|
45
|
+
| Langfuse integration | `@langfuse/openai` wrapper | Manual trace creation | `CallbackHandler` |
|
|
46
|
+
| Realtime API | WebSocket support built-in | Manual WebSocket | N/A |
|
|
47
|
+
| Bundle overhead | ~50KB | 0 | ~200KB+ |
|
|
48
|
+
| Maintenance | Official, rapid updates | None | LangChain release cycle |
|
|
49
|
+
|
|
50
|
+
## API Overview
|
|
51
|
+
|
|
52
|
+
### Client Setup
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import OpenAI from 'openai';
|
|
56
|
+
import { loadConfig } from '../config.js';
|
|
57
|
+
|
|
58
|
+
const config = loadConfig();
|
|
59
|
+
const openai = new OpenAI({
|
|
60
|
+
apiKey: config.openaiApiKey,
|
|
61
|
+
// Optional: custom base URL for Azure OpenAI or local models
|
|
62
|
+
baseURL: config.openaiBaseUrl,
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Chat Completions (Standard)
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
async function chatCompletion(
|
|
70
|
+
messages: OpenAI.ChatCompletionMessageParam[],
|
|
71
|
+
model = 'gpt-5.4'
|
|
72
|
+
): Promise<string> {
|
|
73
|
+
const response = await openai.chat.completions.create({
|
|
74
|
+
model,
|
|
75
|
+
messages,
|
|
76
|
+
temperature: 0.7,
|
|
77
|
+
});
|
|
78
|
+
return response.choices[0]?.message?.content ?? '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Usage in coaiajs
|
|
82
|
+
const analysis = await chatCompletion([
|
|
83
|
+
{ role: 'system', content: 'You are a structural tension chart analyst.' },
|
|
84
|
+
{ role: 'user', content: `Analyze this chart: ${JSON.stringify(chart)}` },
|
|
85
|
+
]);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Streaming Chat Completions
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
async function* streamChat(
|
|
92
|
+
messages: OpenAI.ChatCompletionMessageParam[],
|
|
93
|
+
model = 'gpt-5.4'
|
|
94
|
+
): AsyncGenerator<string> {
|
|
95
|
+
const stream = await openai.chat.completions.create({
|
|
96
|
+
model,
|
|
97
|
+
messages,
|
|
98
|
+
stream: true,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
for await (const chunk of stream) {
|
|
102
|
+
const content = chunk.choices[0]?.delta?.content;
|
|
103
|
+
if (content) yield content;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Usage in CLI for real-time output
|
|
108
|
+
async function streamToTerminal(messages: OpenAI.ChatCompletionMessageParam[]) {
|
|
109
|
+
for await (const chunk of streamChat(messages)) {
|
|
110
|
+
process.stdout.write(chunk);
|
|
111
|
+
}
|
|
112
|
+
process.stdout.write('\n');
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Whisper Transcription (replacing voice-to-text pipeline)
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import fs from 'fs';
|
|
120
|
+
|
|
121
|
+
async function transcribeAudio(filePath: string, language = 'en'): Promise<string> {
|
|
122
|
+
const response = await openai.audio.transcriptions.create({
|
|
123
|
+
file: fs.createReadStream(filePath),
|
|
124
|
+
model: 'whisper-1',
|
|
125
|
+
language,
|
|
126
|
+
response_format: 'text',
|
|
127
|
+
});
|
|
128
|
+
return response;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Verbose transcription with timestamps
|
|
132
|
+
async function transcribeVerbose(filePath: string) {
|
|
133
|
+
const response = await openai.audio.transcriptions.create({
|
|
134
|
+
file: fs.createReadStream(filePath),
|
|
135
|
+
model: 'whisper-1',
|
|
136
|
+
response_format: 'verbose_json',
|
|
137
|
+
timestamp_granularities: ['segment'],
|
|
138
|
+
});
|
|
139
|
+
return response; // includes segments with start/end timestamps
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Image Generation (for chart visualization)
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
async function generateChartImage(description: string): Promise<string> {
|
|
147
|
+
const response = await openai.images.generate({
|
|
148
|
+
model: 'dall-e-3',
|
|
149
|
+
prompt: `Structural tension chart visualization: ${description}`,
|
|
150
|
+
n: 1,
|
|
151
|
+
size: '1024x1024',
|
|
152
|
+
response_format: 'url',
|
|
153
|
+
});
|
|
154
|
+
return response.data[0]?.url ?? '';
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### With Langfuse Tracing (automatic observability)
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
import { observeOpenAI } from '@langfuse/openai';
|
|
162
|
+
import OpenAI from 'openai';
|
|
163
|
+
import { getLangfuse } from '../langfuse/client.js';
|
|
164
|
+
|
|
165
|
+
// Wrap OpenAI client for automatic trace capture
|
|
166
|
+
const tracedOpenai = observeOpenAI(new OpenAI(), {
|
|
167
|
+
langfuse: getLangfuse(),
|
|
168
|
+
generationName: 'coaia-generation',
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Every API call is automatically traced in Langfuse
|
|
172
|
+
// with model, tokens, latency, cost tracking
|
|
173
|
+
const response = await tracedOpenai.chat.completions.create({
|
|
174
|
+
model: 'gpt-5.4',
|
|
175
|
+
messages: [{ role: 'user', content: 'Evaluate this structural tension...' }],
|
|
176
|
+
});
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Structured Output with Zod
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
import { z } from 'zod';
|
|
183
|
+
import { zodResponseFormat } from 'openai/helpers/zod';
|
|
184
|
+
|
|
185
|
+
const ChartAnalysis = z.object({
|
|
186
|
+
tension_score: z.number().min(0).max(10),
|
|
187
|
+
recommendations: z.array(z.string()),
|
|
188
|
+
next_action: z.string(),
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const response = await openai.beta.chat.completions.parse({
|
|
192
|
+
model: 'gpt-5.4',
|
|
193
|
+
messages: [{ role: 'user', content: `Analyze: ${JSON.stringify(chart)}` }],
|
|
194
|
+
response_format: zodResponseFormat(ChartAnalysis, 'chart_analysis'),
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
const analysis = response.choices[0]?.message?.parsed;
|
|
198
|
+
// analysis is fully typed as { tension_score: number; recommendations: string[]; next_action: string }
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Integration Plan
|
|
202
|
+
|
|
203
|
+
1. **Core client:** `src/openai/client.ts` — singleton, config-driven, with Langfuse wrapper
|
|
204
|
+
2. **Chat module:** `src/openai/chat.ts` — standard + streaming completions
|
|
205
|
+
3. **Audio module:** `src/openai/audio.ts` — Whisper transcription (replaces voice pipeline)
|
|
206
|
+
4. **Structured output:** `src/openai/structured.ts` — Zod-validated responses
|
|
207
|
+
5. **MCP tool integration:** Tools that call OpenAI for analysis, decomposition, evaluation
|
|
208
|
+
6. **Pipeline steps:** `src/pipeline/generation-step.ts` — traced generation within pipelines
|
|
209
|
+
|
|
210
|
+
## Version & Ecosystem
|
|
211
|
+
|
|
212
|
+
| Metric | Value |
|
|
213
|
+
|--------|-------|
|
|
214
|
+
| Current version | 6.27.0 (March 2026) |
|
|
215
|
+
| Weekly downloads | ~15M+ |
|
|
216
|
+
| TypeScript | Full native types |
|
|
217
|
+
| Node.js compat | ≥20 LTS |
|
|
218
|
+
| Runtime support | Node.js, Deno, Bun, edge |
|
|
219
|
+
| Streaming | SSE async iterator |
|
|
220
|
+
| Structured output | Zod helper built-in |
|
|
221
|
+
| Models supported | GPT-5.x, GPT-4.x, Whisper, DALL-E, Realtime |
|
|
222
|
+
| Langfuse integration | `@langfuse/openai` drop-in wrapper |
|
|
223
|
+
| License | Apache-2.0 |
|
|
224
|
+
|
|
225
|
+
## References
|
|
226
|
+
|
|
227
|
+
- npm: https://www.npmjs.com/package/openai
|
|
228
|
+
- GitHub: https://github.com/openai/openai-node
|
|
229
|
+
- API docs: https://developers.openai.com/api/docs/libraries/
|
|
230
|
+
- DeepWiki: https://deepwiki.com/openai/openai-node
|
|
231
|
+
- Releases: https://github.com/openai/openai-node/releases
|