cognitive-runtime 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -0
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +200 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.js +7 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.js +78 -0
- package/dist/commands/list.d.ts +5 -0
- package/dist/commands/list.js +28 -0
- package/dist/commands/pipe.d.ts +9 -0
- package/dist/commands/pipe.js +57 -0
- package/dist/commands/run.d.ts +12 -0
- package/dist/commands/run.js +48 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +11 -0
- package/dist/modules/index.d.ts +5 -0
- package/dist/modules/index.js +5 -0
- package/dist/modules/loader.d.ts +8 -0
- package/dist/modules/loader.js +91 -0
- package/dist/modules/runner.d.ts +12 -0
- package/dist/modules/runner.js +93 -0
- package/dist/providers/anthropic.d.ts +14 -0
- package/dist/providers/anthropic.js +70 -0
- package/dist/providers/base.d.ts +11 -0
- package/dist/providers/base.js +19 -0
- package/dist/providers/gemini.d.ts +19 -0
- package/dist/providers/gemini.js +94 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.js +38 -0
- package/dist/providers/openai.d.ts +14 -0
- package/dist/providers/openai.js +67 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.js +4 -0
- package/package.json +33 -0
- package/src/cli.ts +223 -0
- package/src/commands/index.ts +8 -0
- package/src/commands/init.ts +94 -0
- package/src/commands/list.ts +33 -0
- package/src/commands/pipe.ts +74 -0
- package/src/commands/run.ts +65 -0
- package/src/index.ts +39 -0
- package/src/modules/index.ts +6 -0
- package/src/modules/loader.ts +106 -0
- package/src/modules/runner.ts +121 -0
- package/src/providers/anthropic.ts +89 -0
- package/src/providers/base.ts +29 -0
- package/src/providers/gemini.ts +117 -0
- package/src/providers/index.ts +43 -0
- package/src/providers/openai.ts +84 -0
- package/src/types.ts +64 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Loader - Load and parse Cognitive Modules
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from 'node:fs/promises';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import yaml from 'js-yaml';
|
|
7
|
+
const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?/;
|
|
8
|
+
export async function loadModule(modulePath) {
|
|
9
|
+
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
10
|
+
const schemaFile = path.join(modulePath, 'schema.json');
|
|
11
|
+
// Read MODULE.md
|
|
12
|
+
const moduleContent = await fs.readFile(moduleFile, 'utf-8');
|
|
13
|
+
// Parse frontmatter
|
|
14
|
+
const match = moduleContent.match(FRONTMATTER_REGEX);
|
|
15
|
+
if (!match) {
|
|
16
|
+
throw new Error(`Invalid MODULE.md: missing YAML frontmatter in ${moduleFile}`);
|
|
17
|
+
}
|
|
18
|
+
const frontmatter = yaml.load(match[1]);
|
|
19
|
+
const prompt = (match[2] || '').trim();
|
|
20
|
+
// Read schema.json
|
|
21
|
+
let inputSchema;
|
|
22
|
+
let outputSchema;
|
|
23
|
+
try {
|
|
24
|
+
const schemaContent = await fs.readFile(schemaFile, 'utf-8');
|
|
25
|
+
const schema = JSON.parse(schemaContent);
|
|
26
|
+
inputSchema = schema.input;
|
|
27
|
+
outputSchema = schema.output;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Schema file is optional
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
name: frontmatter.name || path.basename(modulePath),
|
|
34
|
+
version: frontmatter.version || '1.0.0',
|
|
35
|
+
responsibility: frontmatter.responsibility || '',
|
|
36
|
+
excludes: frontmatter.excludes || [],
|
|
37
|
+
context: frontmatter.context,
|
|
38
|
+
prompt,
|
|
39
|
+
inputSchema,
|
|
40
|
+
outputSchema,
|
|
41
|
+
location: modulePath,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export async function findModule(name, searchPaths) {
|
|
45
|
+
for (const basePath of searchPaths) {
|
|
46
|
+
const modulePath = path.join(basePath, name);
|
|
47
|
+
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
48
|
+
try {
|
|
49
|
+
await fs.access(moduleFile);
|
|
50
|
+
return await loadModule(modulePath);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Module not found in this path, continue
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
export async function listModules(searchPaths) {
|
|
59
|
+
const modules = [];
|
|
60
|
+
for (const basePath of searchPaths) {
|
|
61
|
+
try {
|
|
62
|
+
const entries = await fs.readdir(basePath, { withFileTypes: true });
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
const modulePath = path.join(basePath, entry.name);
|
|
66
|
+
const moduleFile = path.join(modulePath, 'MODULE.md');
|
|
67
|
+
try {
|
|
68
|
+
await fs.access(moduleFile);
|
|
69
|
+
const module = await loadModule(modulePath);
|
|
70
|
+
modules.push(module);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Not a valid module, skip
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Path doesn't exist, skip
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return modules;
|
|
83
|
+
}
|
|
84
|
+
export function getDefaultSearchPaths(cwd) {
|
|
85
|
+
const home = process.env.HOME || '';
|
|
86
|
+
return [
|
|
87
|
+
path.join(cwd, 'cognitive', 'modules'),
|
|
88
|
+
path.join(cwd, '.cognitive', 'modules'),
|
|
89
|
+
path.join(home, '.cognitive', 'modules'),
|
|
90
|
+
];
|
|
91
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Runner - Execute Cognitive Modules
|
|
3
|
+
*/
|
|
4
|
+
import type { Provider, CognitiveModule, ModuleResult } from '../types.js';
|
|
5
|
+
export interface RunOptions {
|
|
6
|
+
args?: string;
|
|
7
|
+
input?: Record<string, unknown>;
|
|
8
|
+
validateInput?: boolean;
|
|
9
|
+
validateOutput?: boolean;
|
|
10
|
+
verbose?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function runModule(module: CognitiveModule, provider: Provider, options?: RunOptions): Promise<ModuleResult>;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Runner - Execute Cognitive Modules
|
|
3
|
+
*/
|
|
4
|
+
export async function runModule(module, provider, options = {}) {
|
|
5
|
+
const { args, input, verbose = false } = options;
|
|
6
|
+
// Build input data
|
|
7
|
+
let inputData = input || {};
|
|
8
|
+
if (args) {
|
|
9
|
+
inputData = { $ARGUMENTS: args, query: args };
|
|
10
|
+
}
|
|
11
|
+
// Build prompt
|
|
12
|
+
const prompt = buildPrompt(module, inputData);
|
|
13
|
+
if (verbose) {
|
|
14
|
+
console.error('--- Prompt ---');
|
|
15
|
+
console.error(prompt);
|
|
16
|
+
console.error('--- End Prompt ---');
|
|
17
|
+
}
|
|
18
|
+
// Build messages
|
|
19
|
+
const messages = [
|
|
20
|
+
{
|
|
21
|
+
role: 'system',
|
|
22
|
+
content: `You are executing the "${module.name}" Cognitive Module.
|
|
23
|
+
|
|
24
|
+
RESPONSIBILITY: ${module.responsibility}
|
|
25
|
+
|
|
26
|
+
YOU MUST NOT:
|
|
27
|
+
${module.excludes.map(e => `- ${e}`).join('\n')}
|
|
28
|
+
|
|
29
|
+
REQUIRED OUTPUT FORMAT:
|
|
30
|
+
You MUST respond with a valid JSON object. Include these fields:
|
|
31
|
+
- All fields required by the output schema
|
|
32
|
+
- "confidence": a number between 0 and 1
|
|
33
|
+
- "rationale": a string explaining your reasoning
|
|
34
|
+
|
|
35
|
+
Respond with ONLY valid JSON, no markdown code blocks.`,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
role: 'user',
|
|
39
|
+
content: prompt,
|
|
40
|
+
},
|
|
41
|
+
];
|
|
42
|
+
// Invoke provider
|
|
43
|
+
const result = await provider.invoke({
|
|
44
|
+
messages,
|
|
45
|
+
jsonSchema: module.outputSchema,
|
|
46
|
+
temperature: 0.3,
|
|
47
|
+
});
|
|
48
|
+
if (verbose) {
|
|
49
|
+
console.error('--- Response ---');
|
|
50
|
+
console.error(result.content);
|
|
51
|
+
console.error('--- End Response ---');
|
|
52
|
+
}
|
|
53
|
+
// Parse response
|
|
54
|
+
let output;
|
|
55
|
+
try {
|
|
56
|
+
// Try to extract JSON from markdown code blocks
|
|
57
|
+
const jsonMatch = result.content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
58
|
+
const jsonStr = jsonMatch ? jsonMatch[1] : result.content;
|
|
59
|
+
output = JSON.parse(jsonStr.trim());
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
throw new Error(`Failed to parse JSON response: ${result.content.substring(0, 500)}`);
|
|
63
|
+
}
|
|
64
|
+
// Extract confidence and rationale
|
|
65
|
+
const outputObj = output;
|
|
66
|
+
const confidence = typeof outputObj.confidence === 'number' ? outputObj.confidence : 0.5;
|
|
67
|
+
const rationale = typeof outputObj.rationale === 'string' ? outputObj.rationale : '';
|
|
68
|
+
return {
|
|
69
|
+
output,
|
|
70
|
+
confidence,
|
|
71
|
+
rationale,
|
|
72
|
+
raw: result.content,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function buildPrompt(module, inputData) {
|
|
76
|
+
let prompt = module.prompt;
|
|
77
|
+
// Substitute $ARGUMENTS
|
|
78
|
+
const argsValue = String(inputData.$ARGUMENTS || inputData.query || '');
|
|
79
|
+
prompt = prompt.replace(/\$ARGUMENTS/g, argsValue);
|
|
80
|
+
// Substitute $N placeholders
|
|
81
|
+
const argsList = argsValue.split(/\s+/);
|
|
82
|
+
argsList.forEach((arg, i) => {
|
|
83
|
+
prompt = prompt.replace(new RegExp(`\\$${i}`, 'g'), arg);
|
|
84
|
+
prompt = prompt.replace(new RegExp(`\\$ARGUMENTS\\[${i}\\]`, 'g'), arg);
|
|
85
|
+
});
|
|
86
|
+
// Substitute other input fields
|
|
87
|
+
for (const [key, value] of Object.entries(inputData)) {
|
|
88
|
+
if (key !== '$ARGUMENTS' && key !== 'query') {
|
|
89
|
+
prompt = prompt.replace(new RegExp(`\\$\\{${key}\\}`, 'g'), String(value));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return prompt;
|
|
93
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Provider - Claude API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class AnthropicProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Provider - Claude API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class AnthropicProvider extends BaseProvider {
|
|
6
|
+
name = 'anthropic';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl = 'https://api.anthropic.com/v1';
|
|
10
|
+
constructor(apiKey, model = 'claude-sonnet-4-20250514') {
|
|
11
|
+
super();
|
|
12
|
+
this.apiKey = apiKey || process.env.ANTHROPIC_API_KEY || '';
|
|
13
|
+
this.model = model;
|
|
14
|
+
}
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return !!this.apiKey;
|
|
17
|
+
}
|
|
18
|
+
async invoke(params) {
|
|
19
|
+
if (!this.isConfigured()) {
|
|
20
|
+
throw new Error('Anthropic API key not configured. Set ANTHROPIC_API_KEY environment variable.');
|
|
21
|
+
}
|
|
22
|
+
const url = `${this.baseUrl}/messages`;
|
|
23
|
+
// Extract system message
|
|
24
|
+
const systemMessage = params.messages.find(m => m.role === 'system');
|
|
25
|
+
const otherMessages = params.messages.filter(m => m.role !== 'system');
|
|
26
|
+
// Add JSON schema instruction if provided
|
|
27
|
+
let messages = otherMessages;
|
|
28
|
+
if (params.jsonSchema) {
|
|
29
|
+
const lastUserIdx = messages.findLastIndex(m => m.role === 'user');
|
|
30
|
+
if (lastUserIdx >= 0) {
|
|
31
|
+
messages = [...messages];
|
|
32
|
+
messages[lastUserIdx] = {
|
|
33
|
+
...messages[lastUserIdx],
|
|
34
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const body = {
|
|
39
|
+
model: this.model,
|
|
40
|
+
messages: messages.map(m => ({ role: m.role, content: m.content })),
|
|
41
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
42
|
+
};
|
|
43
|
+
if (systemMessage) {
|
|
44
|
+
body.system = systemMessage.content;
|
|
45
|
+
}
|
|
46
|
+
const response = await fetch(url, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: {
|
|
49
|
+
'Content-Type': 'application/json',
|
|
50
|
+
'x-api-key': this.apiKey,
|
|
51
|
+
'anthropic-version': '2023-06-01',
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify(body),
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const error = await response.text();
|
|
57
|
+
throw new Error(`Anthropic API error: ${response.status} - ${error}`);
|
|
58
|
+
}
|
|
59
|
+
const data = await response.json();
|
|
60
|
+
const content = data.content?.[0]?.text || '';
|
|
61
|
+
return {
|
|
62
|
+
content,
|
|
63
|
+
usage: data.usage ? {
|
|
64
|
+
promptTokens: data.usage.input_tokens || 0,
|
|
65
|
+
completionTokens: data.usage.output_tokens || 0,
|
|
66
|
+
totalTokens: (data.usage.input_tokens || 0) + (data.usage.output_tokens || 0),
|
|
67
|
+
} : undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Provider - Abstract class for all LLM providers
|
|
3
|
+
*/
|
|
4
|
+
import type { Provider, InvokeParams, InvokeResult } from '../types.js';
|
|
5
|
+
export declare abstract class BaseProvider implements Provider {
|
|
6
|
+
abstract name: string;
|
|
7
|
+
abstract invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
8
|
+
abstract isConfigured(): boolean;
|
|
9
|
+
protected buildJsonPrompt(schema: object): string;
|
|
10
|
+
protected parseJsonResponse(content: string): unknown;
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Provider - Abstract class for all LLM providers
|
|
3
|
+
*/
|
|
4
|
+
export class BaseProvider {
|
|
5
|
+
buildJsonPrompt(schema) {
|
|
6
|
+
return `\n\nYou MUST respond with valid JSON matching this schema:\n${JSON.stringify(schema, null, 2)}\n\nRespond with ONLY the JSON, no markdown code blocks.`;
|
|
7
|
+
}
|
|
8
|
+
parseJsonResponse(content) {
|
|
9
|
+
// Try to extract JSON from markdown code blocks
|
|
10
|
+
const jsonMatch = content.match(/```(?:json)?\s*([\s\S]*?)\s*```/);
|
|
11
|
+
const jsonStr = jsonMatch ? jsonMatch[1] : content;
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(jsonStr.trim());
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
throw new Error(`Failed to parse JSON response: ${content.substring(0, 200)}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Provider - Google Gemini API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class GeminiProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
/**
|
|
14
|
+
* Clean JSON Schema for Gemini API compatibility
|
|
15
|
+
* Removes unsupported fields like additionalProperties
|
|
16
|
+
*/
|
|
17
|
+
private cleanSchemaForGemini;
|
|
18
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini Provider - Google Gemini API
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class GeminiProvider extends BaseProvider {
|
|
6
|
+
name = 'gemini';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl = 'https://generativelanguage.googleapis.com/v1beta';
|
|
10
|
+
constructor(apiKey, model = 'gemini-2.0-flash') {
|
|
11
|
+
super();
|
|
12
|
+
this.apiKey = apiKey || process.env.GEMINI_API_KEY || '';
|
|
13
|
+
this.model = model;
|
|
14
|
+
}
|
|
15
|
+
isConfigured() {
|
|
16
|
+
return !!this.apiKey;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Clean JSON Schema for Gemini API compatibility
|
|
20
|
+
* Removes unsupported fields like additionalProperties
|
|
21
|
+
*/
|
|
22
|
+
cleanSchemaForGemini(schema) {
|
|
23
|
+
const unsupportedFields = ['additionalProperties', '$schema', 'default', 'examples'];
|
|
24
|
+
const clean = (obj) => {
|
|
25
|
+
if (Array.isArray(obj)) {
|
|
26
|
+
return obj.map(clean);
|
|
27
|
+
}
|
|
28
|
+
if (obj && typeof obj === 'object') {
|
|
29
|
+
const result = {};
|
|
30
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
31
|
+
if (!unsupportedFields.includes(key)) {
|
|
32
|
+
result[key] = clean(value);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
return obj;
|
|
38
|
+
};
|
|
39
|
+
return clean(schema);
|
|
40
|
+
}
|
|
41
|
+
async invoke(params) {
|
|
42
|
+
if (!this.isConfigured()) {
|
|
43
|
+
throw new Error('Gemini API key not configured. Set GEMINI_API_KEY environment variable.');
|
|
44
|
+
}
|
|
45
|
+
const url = `${this.baseUrl}/models/${this.model}:generateContent?key=${this.apiKey}`;
|
|
46
|
+
// Convert messages to Gemini format
|
|
47
|
+
const contents = params.messages
|
|
48
|
+
.filter(m => m.role !== 'system')
|
|
49
|
+
.map(m => ({
|
|
50
|
+
role: m.role === 'assistant' ? 'model' : 'user',
|
|
51
|
+
parts: [{ text: m.content }]
|
|
52
|
+
}));
|
|
53
|
+
// Add system instruction if present
|
|
54
|
+
const systemMessage = params.messages.find(m => m.role === 'system');
|
|
55
|
+
const body = {
|
|
56
|
+
contents,
|
|
57
|
+
generationConfig: {
|
|
58
|
+
temperature: params.temperature ?? 0.7,
|
|
59
|
+
maxOutputTokens: params.maxTokens ?? 8192,
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
if (systemMessage) {
|
|
63
|
+
body.systemInstruction = { parts: [{ text: systemMessage.content }] };
|
|
64
|
+
}
|
|
65
|
+
// Add JSON schema constraint if provided
|
|
66
|
+
if (params.jsonSchema) {
|
|
67
|
+
const cleanedSchema = this.cleanSchemaForGemini(params.jsonSchema);
|
|
68
|
+
body.generationConfig = {
|
|
69
|
+
...body.generationConfig,
|
|
70
|
+
responseMimeType: 'application/json',
|
|
71
|
+
responseSchema: cleanedSchema,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const response = await fetch(url, {
|
|
75
|
+
method: 'POST',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify(body),
|
|
78
|
+
});
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const error = await response.text();
|
|
81
|
+
throw new Error(`Gemini API error: ${response.status} - ${error}`);
|
|
82
|
+
}
|
|
83
|
+
const data = await response.json();
|
|
84
|
+
const content = data.candidates?.[0]?.content?.parts?.[0]?.text || '';
|
|
85
|
+
return {
|
|
86
|
+
content,
|
|
87
|
+
usage: data.usageMetadata ? {
|
|
88
|
+
promptTokens: data.usageMetadata.promptTokenCount || 0,
|
|
89
|
+
completionTokens: data.usageMetadata.candidatesTokenCount || 0,
|
|
90
|
+
totalTokens: data.usageMetadata.totalTokenCount || 0,
|
|
91
|
+
} : undefined,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Registry
|
|
3
|
+
*/
|
|
4
|
+
import type { Provider } from '../types.js';
|
|
5
|
+
export { BaseProvider } from './base.js';
|
|
6
|
+
export { GeminiProvider } from './gemini.js';
|
|
7
|
+
export { OpenAIProvider } from './openai.js';
|
|
8
|
+
export { AnthropicProvider } from './anthropic.js';
|
|
9
|
+
export declare function getProvider(name?: string): Provider;
|
|
10
|
+
export declare function listProviders(): Array<{
|
|
11
|
+
name: string;
|
|
12
|
+
configured: boolean;
|
|
13
|
+
}>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Registry
|
|
3
|
+
*/
|
|
4
|
+
import { GeminiProvider } from './gemini.js';
|
|
5
|
+
import { OpenAIProvider } from './openai.js';
|
|
6
|
+
import { AnthropicProvider } from './anthropic.js';
|
|
7
|
+
export { BaseProvider } from './base.js';
|
|
8
|
+
export { GeminiProvider } from './gemini.js';
|
|
9
|
+
export { OpenAIProvider } from './openai.js';
|
|
10
|
+
export { AnthropicProvider } from './anthropic.js';
|
|
11
|
+
const providers = {
|
|
12
|
+
gemini: () => new GeminiProvider(),
|
|
13
|
+
openai: () => new OpenAIProvider(),
|
|
14
|
+
anthropic: () => new AnthropicProvider(),
|
|
15
|
+
};
|
|
16
|
+
export function getProvider(name) {
|
|
17
|
+
// Auto-detect if not specified
|
|
18
|
+
if (!name) {
|
|
19
|
+
if (process.env.GEMINI_API_KEY)
|
|
20
|
+
return new GeminiProvider();
|
|
21
|
+
if (process.env.OPENAI_API_KEY)
|
|
22
|
+
return new OpenAIProvider();
|
|
23
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
24
|
+
return new AnthropicProvider();
|
|
25
|
+
throw new Error('No LLM provider configured. Set GEMINI_API_KEY, OPENAI_API_KEY, or ANTHROPIC_API_KEY.');
|
|
26
|
+
}
|
|
27
|
+
const factory = providers[name.toLowerCase()];
|
|
28
|
+
if (!factory) {
|
|
29
|
+
throw new Error(`Unknown provider: ${name}. Available: ${Object.keys(providers).join(', ')}`);
|
|
30
|
+
}
|
|
31
|
+
return factory();
|
|
32
|
+
}
|
|
33
|
+
export function listProviders() {
|
|
34
|
+
return Object.entries(providers).map(([name, factory]) => ({
|
|
35
|
+
name,
|
|
36
|
+
configured: factory().isConfigured(),
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Provider - OpenAI API (and compatible APIs)
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
import type { InvokeParams, InvokeResult } from '../types.js';
|
|
6
|
+
export declare class OpenAIProvider extends BaseProvider {
|
|
7
|
+
name: string;
|
|
8
|
+
private apiKey;
|
|
9
|
+
private model;
|
|
10
|
+
private baseUrl;
|
|
11
|
+
constructor(apiKey?: string, model?: string, baseUrl?: string);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Provider - OpenAI API (and compatible APIs)
|
|
3
|
+
*/
|
|
4
|
+
import { BaseProvider } from './base.js';
|
|
5
|
+
export class OpenAIProvider extends BaseProvider {
|
|
6
|
+
name = 'openai';
|
|
7
|
+
apiKey;
|
|
8
|
+
model;
|
|
9
|
+
baseUrl;
|
|
10
|
+
constructor(apiKey, model = 'gpt-4o', baseUrl = 'https://api.openai.com/v1') {
|
|
11
|
+
super();
|
|
12
|
+
this.apiKey = apiKey || process.env.OPENAI_API_KEY || '';
|
|
13
|
+
this.model = model;
|
|
14
|
+
this.baseUrl = baseUrl;
|
|
15
|
+
}
|
|
16
|
+
isConfigured() {
|
|
17
|
+
return !!this.apiKey;
|
|
18
|
+
}
|
|
19
|
+
async invoke(params) {
|
|
20
|
+
if (!this.isConfigured()) {
|
|
21
|
+
throw new Error('OpenAI API key not configured. Set OPENAI_API_KEY environment variable.');
|
|
22
|
+
}
|
|
23
|
+
const url = `${this.baseUrl}/chat/completions`;
|
|
24
|
+
const body = {
|
|
25
|
+
model: this.model,
|
|
26
|
+
messages: params.messages,
|
|
27
|
+
temperature: params.temperature ?? 0.7,
|
|
28
|
+
max_tokens: params.maxTokens ?? 4096,
|
|
29
|
+
};
|
|
30
|
+
// Add JSON mode if schema provided
|
|
31
|
+
if (params.jsonSchema) {
|
|
32
|
+
body.response_format = { type: 'json_object' };
|
|
33
|
+
// Append schema instruction to last user message
|
|
34
|
+
const lastUserIdx = params.messages.findLastIndex(m => m.role === 'user');
|
|
35
|
+
if (lastUserIdx >= 0) {
|
|
36
|
+
const messages = [...params.messages];
|
|
37
|
+
messages[lastUserIdx] = {
|
|
38
|
+
...messages[lastUserIdx],
|
|
39
|
+
content: messages[lastUserIdx].content + this.buildJsonPrompt(params.jsonSchema),
|
|
40
|
+
};
|
|
41
|
+
body.messages = messages;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const response = await fetch(url, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(body),
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const error = await response.text();
|
|
54
|
+
throw new Error(`OpenAI API error: ${response.status} - ${error}`);
|
|
55
|
+
}
|
|
56
|
+
const data = await response.json();
|
|
57
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
58
|
+
return {
|
|
59
|
+
content,
|
|
60
|
+
usage: data.usage ? {
|
|
61
|
+
promptTokens: data.usage.prompt_tokens || 0,
|
|
62
|
+
completionTokens: data.usage.completion_tokens || 0,
|
|
63
|
+
totalTokens: data.usage.total_tokens || 0,
|
|
64
|
+
} : undefined,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cognitive Runtime - Core Types
|
|
3
|
+
*/
|
|
4
|
+
export interface Provider {
|
|
5
|
+
name: string;
|
|
6
|
+
invoke(params: InvokeParams): Promise<InvokeResult>;
|
|
7
|
+
isConfigured(): boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface InvokeParams {
|
|
10
|
+
messages: Message[];
|
|
11
|
+
jsonSchema?: object;
|
|
12
|
+
temperature?: number;
|
|
13
|
+
maxTokens?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface Message {
|
|
16
|
+
role: 'system' | 'user' | 'assistant';
|
|
17
|
+
content: string;
|
|
18
|
+
}
|
|
19
|
+
export interface InvokeResult {
|
|
20
|
+
content: string;
|
|
21
|
+
usage?: {
|
|
22
|
+
promptTokens: number;
|
|
23
|
+
completionTokens: number;
|
|
24
|
+
totalTokens: number;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export interface CognitiveModule {
|
|
28
|
+
name: string;
|
|
29
|
+
version: string;
|
|
30
|
+
responsibility: string;
|
|
31
|
+
excludes: string[];
|
|
32
|
+
context?: 'fork' | 'main';
|
|
33
|
+
prompt: string;
|
|
34
|
+
inputSchema?: object;
|
|
35
|
+
outputSchema?: object;
|
|
36
|
+
location: string;
|
|
37
|
+
}
|
|
38
|
+
export interface ModuleResult {
|
|
39
|
+
output: unknown;
|
|
40
|
+
confidence: number;
|
|
41
|
+
rationale: string;
|
|
42
|
+
raw?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface CommandContext {
|
|
45
|
+
cwd: string;
|
|
46
|
+
provider: Provider;
|
|
47
|
+
verbose?: boolean;
|
|
48
|
+
}
|
|
49
|
+
export interface CommandResult {
|
|
50
|
+
success: boolean;
|
|
51
|
+
data?: unknown;
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cognitive-runtime",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Cognitive Runtime - Structured AI Task Execution",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"cog": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/cli.js",
|
|
13
|
+
"dev": "tsx src/cli.ts"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cognitive",
|
|
17
|
+
"ai",
|
|
18
|
+
"llm",
|
|
19
|
+
"structured",
|
|
20
|
+
"modules"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"js-yaml": "^4.1.1",
|
|
26
|
+
"tsx": "^4.21.0",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/js-yaml": "^4.0.9",
|
|
31
|
+
"@types/node": "^25.1.0"
|
|
32
|
+
}
|
|
33
|
+
}
|