@acip/cli 1.5.5 → 1.6.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.
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +8 -0
- package/dist/utils/project.js.map +1 -1
- package/package.json +1 -1
- package/templates/.env.example.template +7 -0
- package/templates/advanced/src/components/assistant.js.template +311 -29
- package/templates/advanced/src/components/assistant.ts.template +322 -42
- package/templates/advanced/src/index.js.template +112 -29
- package/templates/advanced/src/index.ts.template +114 -34
- package/templates/basic/src/index.js.template +188 -19
- package/templates/basic/src/index.ts.template +187 -19
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
import { createCore } from '@acip/core';
|
|
2
3
|
import { ModelInvocationModule, type ModelInvocationConfig } from '@acip/model-invocation';
|
|
3
|
-
import { createAssistant } from './components/assistant.js';
|
|
4
|
+
import { createAssistant, type AssistantModules } from './components/assistant.js';
|
|
4
5
|
|
|
5
6
|
type ProviderConfigWithEnabled = ModelInvocationConfig['providers'][number] & { enabled: boolean };
|
|
6
7
|
|
|
@@ -8,66 +9,145 @@ async function main(): Promise<void> {
|
|
|
8
9
|
const apiKey = process.env.OPENAI_API_KEY || process.env.ACIP_API_KEY;
|
|
9
10
|
const baseUrl = process.env.OPENAI_BASE_URL || process.env.ACIP_BASE_URL;
|
|
10
11
|
const modelId = process.env.DEFAULT_MODEL_ID || 'gpt-5.2';
|
|
12
|
+
const requestTimeoutMs = Number(process.env.ACIP_REQUEST_TIMEOUT_MS || '180000');
|
|
11
13
|
|
|
12
14
|
if (!apiKey) {
|
|
13
|
-
|
|
15
|
+
console.error('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
|
|
16
|
+
process.exit(1);
|
|
14
17
|
}
|
|
15
18
|
|
|
19
|
+
// ── 1. ACIP Core ──
|
|
20
|
+
const core = createCore();
|
|
21
|
+
console.log(`[core] initialized (instance: ${core.instanceId})`);
|
|
22
|
+
|
|
23
|
+
// ── 2. Model Invocation ──
|
|
16
24
|
const provider: ProviderConfigWithEnabled = {
|
|
17
25
|
name: 'openai',
|
|
18
26
|
apiKey,
|
|
19
27
|
baseUrl,
|
|
20
28
|
enabled: true,
|
|
21
|
-
models: [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
29
|
-
]
|
|
29
|
+
models: [{
|
|
30
|
+
id: modelId,
|
|
31
|
+
name: modelId,
|
|
32
|
+
provider: 'openai',
|
|
33
|
+
capabilities: ['chat'],
|
|
34
|
+
parameters: {}
|
|
35
|
+
}]
|
|
30
36
|
};
|
|
31
37
|
|
|
32
|
-
const
|
|
38
|
+
const miConfig: ModelInvocationConfig = {
|
|
33
39
|
providers: [provider],
|
|
34
40
|
caching: { enabled: true, ttl: 600, maxSize: 100, strategy: 'lru' },
|
|
35
41
|
observability: { enabled: true, metricsEnabled: true, tracingEnabled: false, exportInterval: 60000 },
|
|
36
|
-
streaming: { enabled:
|
|
42
|
+
streaming: { enabled: true, chunkSize: 1, timeout: requestTimeoutMs }
|
|
37
43
|
};
|
|
38
44
|
|
|
39
|
-
const modelInvocation = new ModelInvocationModule(
|
|
45
|
+
const modelInvocation = new ModelInvocationModule(miConfig);
|
|
40
46
|
|
|
41
47
|
await new Promise<void>((resolve, reject) => {
|
|
42
48
|
modelInvocation.once('ready', () => resolve());
|
|
43
49
|
modelInvocation.once('error', (error) => reject(error));
|
|
44
50
|
});
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
modelInvocation.on('requestStarted', (data) => {
|
|
48
|
-
console.log(`Model invocation started: ${data.requestId}`);
|
|
49
|
-
});
|
|
52
|
+
console.log('[model-invocation] ready');
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
// ── 3. Context Management (降级运行) ──
|
|
55
|
+
let contextManager: any = null;
|
|
56
|
+
let contextId: string | null = null;
|
|
57
|
+
try {
|
|
58
|
+
const { ContextManager } = await import('@acip/context-management');
|
|
59
|
+
contextManager = new ContextManager(
|
|
60
|
+
core.instanceId,
|
|
61
|
+
core.stateManager,
|
|
62
|
+
core.telemetryService,
|
|
63
|
+
core.eventManager
|
|
64
|
+
);
|
|
65
|
+
await contextManager.initialize();
|
|
66
|
+
const ctx = await contextManager.createContext('main-conversation');
|
|
67
|
+
contextId = 'main-conversation';
|
|
68
|
+
console.log('[context-management] ready');
|
|
69
|
+
} catch (err: any) {
|
|
70
|
+
console.warn(`[context-management] skipped: ${err.message}`);
|
|
71
|
+
}
|
|
54
72
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
73
|
+
// ── 4. Data Access (降级运行) ──
|
|
74
|
+
let dataAccess: any = null;
|
|
75
|
+
try {
|
|
76
|
+
const { DataAccessModule, InMemoryAdapter } = await import('@acip/data-access');
|
|
77
|
+
dataAccess = new DataAccessModule();
|
|
78
|
+
const adapter = new InMemoryAdapter();
|
|
79
|
+
await dataAccess.registerSource('notes', { adapter });
|
|
80
|
+
console.log('[data-access] ready');
|
|
81
|
+
} catch (err: any) {
|
|
82
|
+
console.warn(`[data-access] skipped: ${err.message}`);
|
|
83
|
+
}
|
|
60
84
|
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
85
|
+
// ── 5. Blockchain + Audit (降级运行) ──
|
|
86
|
+
let blockchain: any = null;
|
|
87
|
+
let auditService: any = null;
|
|
88
|
+
try {
|
|
89
|
+
const { Blockchain, AuditService } = await import('@acip/blockchain');
|
|
90
|
+
blockchain = new Blockchain();
|
|
91
|
+
auditService = new AuditService(blockchain);
|
|
92
|
+
console.log('[blockchain] ready');
|
|
93
|
+
} catch (err: any) {
|
|
94
|
+
console.warn(`[blockchain] skipped: ${err.message}`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── 6. Security & Authentication (降级运行) ──
|
|
98
|
+
let authManager: any = null;
|
|
99
|
+
try {
|
|
100
|
+
const { AuthManager } = await import('@acip/security-authentication');
|
|
101
|
+
authManager = new AuthManager({
|
|
102
|
+
passwordPolicy: {
|
|
103
|
+
minLength: 4,
|
|
104
|
+
requireNumbers: false,
|
|
105
|
+
requireLowercase: false,
|
|
106
|
+
requireUppercase: false,
|
|
107
|
+
requireSpecial: false,
|
|
108
|
+
maxRepeatingChars: 0,
|
|
109
|
+
preventCommonPasswords: false
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
await authManager.initialize({
|
|
113
|
+
jwtSecret: process.env.JWT_SECRET || 'acip-demo-secret'
|
|
114
|
+
});
|
|
115
|
+
await authManager.start();
|
|
116
|
+
|
|
117
|
+
// 注册 demo 用户
|
|
118
|
+
const adminPassword = process.env.ADMIN_PASSWORD || 'admin';
|
|
119
|
+
try {
|
|
120
|
+
await authManager.registerUser('admin', {
|
|
121
|
+
password: adminPassword,
|
|
122
|
+
roles: ['admin']
|
|
123
|
+
});
|
|
124
|
+
const auth = await authManager.authenticate('admin', adminPassword);
|
|
125
|
+
if (auth.authenticated) {
|
|
126
|
+
console.log('[security] ready — admin authenticated');
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
// 用户可能已存在
|
|
130
|
+
}
|
|
131
|
+
console.log('[security-authentication] ready');
|
|
132
|
+
} catch (err: any) {
|
|
133
|
+
console.warn(`[security-authentication] skipped: ${err.message}`);
|
|
134
|
+
}
|
|
65
135
|
|
|
66
|
-
|
|
136
|
+
// ── 启动交互式助手 ──
|
|
137
|
+
const modules: AssistantModules = {
|
|
138
|
+
modelInvocation,
|
|
139
|
+
modelId,
|
|
140
|
+
provider: 'openai',
|
|
141
|
+
contextManager,
|
|
142
|
+
contextId,
|
|
143
|
+
blockchain,
|
|
144
|
+
auditService,
|
|
145
|
+
dataAccess,
|
|
146
|
+
authManager
|
|
147
|
+
};
|
|
67
148
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
console.log(`Conversation has ${history.length} messages`);
|
|
149
|
+
const assistant = createAssistant(modules);
|
|
150
|
+
assistant.startREPL();
|
|
71
151
|
}
|
|
72
152
|
|
|
73
153
|
main().catch(console.error);
|
|
@@ -1,36 +1,45 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import { createCore } from '@acip/core';
|
|
2
4
|
import { ModelInvocationModule } from '@acip/model-invocation';
|
|
3
5
|
|
|
6
|
+
const SYSTEM_PROMPT = `You are ACIP Assistant, an AI powered by the Adaptive Contextual Intelligence Protocol (ACIP). ACIP is a distributed AI communication protocol providing semantic routing, resource-aware scheduling, and edge computing support. Help users understand and build with ACIP.`;
|
|
7
|
+
|
|
4
8
|
async function main() {
|
|
5
9
|
const apiKey = process.env.OPENAI_API_KEY || process.env.ACIP_API_KEY;
|
|
6
10
|
const baseUrl = process.env.OPENAI_BASE_URL || process.env.ACIP_BASE_URL;
|
|
7
11
|
const modelId = process.env.DEFAULT_MODEL_ID || 'gpt-5.2';
|
|
12
|
+
const requestTimeoutMs = Number(process.env.ACIP_REQUEST_TIMEOUT_MS || '180000');
|
|
8
13
|
|
|
9
14
|
if (!apiKey) {
|
|
10
|
-
|
|
15
|
+
console.error('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
|
|
16
|
+
process.exit(1);
|
|
11
17
|
}
|
|
12
18
|
|
|
19
|
+
// 初始化 ACIP Core
|
|
20
|
+
const core = createCore();
|
|
21
|
+
console.log(`ACIP Core initialized (instance: ${core.instanceId})`);
|
|
22
|
+
|
|
23
|
+
// 配置模型调用
|
|
13
24
|
const provider = {
|
|
14
25
|
name: 'openai',
|
|
15
26
|
apiKey,
|
|
16
27
|
baseUrl,
|
|
17
28
|
enabled: true,
|
|
18
|
-
models: [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
]
|
|
29
|
+
models: [{
|
|
30
|
+
id: modelId,
|
|
31
|
+
name: modelId,
|
|
32
|
+
provider: 'openai',
|
|
33
|
+
capabilities: ['chat'],
|
|
34
|
+
parameters: {}
|
|
35
|
+
}]
|
|
27
36
|
};
|
|
28
37
|
|
|
29
38
|
const config = {
|
|
30
39
|
providers: [provider],
|
|
31
40
|
caching: { enabled: false, ttl: 0, maxSize: 0, strategy: 'lru' },
|
|
32
41
|
observability: { enabled: false, metricsEnabled: false, tracingEnabled: false, exportInterval: 0 },
|
|
33
|
-
streaming: { enabled:
|
|
42
|
+
streaming: { enabled: true, chunkSize: 1, timeout: requestTimeoutMs }
|
|
34
43
|
};
|
|
35
44
|
|
|
36
45
|
const modelInvocation = new ModelInvocationModule(config);
|
|
@@ -40,16 +49,176 @@ async function main() {
|
|
|
40
49
|
modelInvocation.once('error', (error) => reject(error));
|
|
41
50
|
});
|
|
42
51
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
console.log('Model Invocation ready');
|
|
53
|
+
|
|
54
|
+
// 对话历史
|
|
55
|
+
const history = [];
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
const resolveModelsUrl = (rawBaseUrl) => {
|
|
59
|
+
const fallback = 'https://api.openai.com/v1';
|
|
60
|
+
const trimmed = (rawBaseUrl || fallback).replace(/\/$/, '');
|
|
61
|
+
if (trimmed.endsWith('/models')) {
|
|
62
|
+
return trimmed;
|
|
63
|
+
}
|
|
64
|
+
if (trimmed.endsWith('/v1')) {
|
|
65
|
+
return `${trimmed}/models`;
|
|
66
|
+
}
|
|
67
|
+
return `${trimmed}/v1/models`;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const listModels = async () => {
|
|
71
|
+
if (!apiKey) {
|
|
72
|
+
console.log('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
|
|
73
|
+
console.log('');
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const modelsUrl = resolveModelsUrl(baseUrl);
|
|
78
|
+
try {
|
|
79
|
+
const response = await fetch(modelsUrl, {
|
|
80
|
+
method: 'GET',
|
|
81
|
+
headers: {
|
|
82
|
+
'Authorization': `Bearer ${apiKey}`
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
88
|
+
try {
|
|
89
|
+
const error = await response.json();
|
|
90
|
+
errorMessage = error?.error?.message || error?.message || errorMessage;
|
|
91
|
+
} catch {
|
|
92
|
+
const text = await response.text();
|
|
93
|
+
if (text) {
|
|
94
|
+
errorMessage = text;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
console.log(`Models list error: ${errorMessage}`);
|
|
98
|
+
console.log('');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const data = await response.json();
|
|
103
|
+
const models = Array.isArray(data?.data)
|
|
104
|
+
? data.data
|
|
105
|
+
: Array.isArray(data?.models)
|
|
106
|
+
? data.models
|
|
107
|
+
: [];
|
|
108
|
+
|
|
109
|
+
if (models.length === 0) {
|
|
110
|
+
console.log('No models returned.');
|
|
111
|
+
console.log('');
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log('');
|
|
116
|
+
console.log(`--- Models (${models.length}) ---`);
|
|
117
|
+
for (const model of models) {
|
|
118
|
+
const id = model?.id || model?.name || model?.model || JSON.stringify(model);
|
|
119
|
+
console.log(` - ${id}`);
|
|
120
|
+
}
|
|
121
|
+
console.log('---');
|
|
122
|
+
console.log('');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.log(`Models list error: ${error.message}`);
|
|
125
|
+
console.log('');
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// 创建 REPL
|
|
130
|
+
const rl = readline.createInterface({
|
|
131
|
+
input: process.stdin,
|
|
132
|
+
output: process.stdout
|
|
50
133
|
});
|
|
51
134
|
|
|
52
|
-
console.log('
|
|
135
|
+
console.log('\n=== ACIP Assistant ===');
|
|
136
|
+
console.log('Type your message to chat with AI.');
|
|
137
|
+
console.log('Commands: /help, /models, /clear, /exit\n');
|
|
138
|
+
|
|
139
|
+
const prompt = () => {
|
|
140
|
+
rl.question('You> ', async (input) => {
|
|
141
|
+
const trimmed = input.trim();
|
|
142
|
+
if (!trimmed) { prompt(); return; }
|
|
143
|
+
|
|
144
|
+
if (trimmed === '/exit' || trimmed === '/quit') {
|
|
145
|
+
console.log('Goodbye!');
|
|
146
|
+
rl.close();
|
|
147
|
+
process.exit(0);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (trimmed === '/help') {
|
|
151
|
+
console.log('\nCommands:');
|
|
152
|
+
console.log(' /help - Show this help');
|
|
153
|
+
console.log(' /models - List available models');
|
|
154
|
+
console.log(' /clear - Clear conversation history');
|
|
155
|
+
console.log(' /exit - Exit the application\n');
|
|
156
|
+
prompt();
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (trimmed === '/models') {
|
|
161
|
+
await listModels();
|
|
162
|
+
prompt();
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (trimmed === '/clear') {
|
|
167
|
+
history.length = 0;
|
|
168
|
+
console.log('Conversation history cleared.\n');
|
|
169
|
+
prompt();
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const messages = [
|
|
175
|
+
{ role: 'system', content: SYSTEM_PROMPT },
|
|
176
|
+
...history,
|
|
177
|
+
{ role: 'user', content: trimmed }
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
const responseContent = await new Promise((resolve, reject) => {
|
|
181
|
+
const stream = modelInvocation.invokeStream({
|
|
182
|
+
model: modelId,
|
|
183
|
+
provider: 'openai',
|
|
184
|
+
timeout: requestTimeoutMs,
|
|
185
|
+
messages
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
let full = '';
|
|
189
|
+
process.stdout.write('\nAssistant> ');
|
|
190
|
+
|
|
191
|
+
stream.on('data', (chunk) => {
|
|
192
|
+
const content = chunk?.output?.content ?? chunk?.content;
|
|
193
|
+
if (typeof content === 'string') {
|
|
194
|
+
full += content;
|
|
195
|
+
process.stdout.write(content);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
stream.on('end', () => {
|
|
200
|
+
process.stdout.write('\n\n');
|
|
201
|
+
resolve(full);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
stream.on('error', (err) => {
|
|
205
|
+
const message = err?.error || err?.message || 'Unknown error';
|
|
206
|
+
process.stdout.write('\n');
|
|
207
|
+
reject(new Error(message));
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
history.push({ role: 'user', content: trimmed });
|
|
212
|
+
history.push({ role: 'assistant', content: responseContent });
|
|
213
|
+
} catch (error) {
|
|
214
|
+
console.error(`\nError: ${error.message}\n`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
prompt();
|
|
218
|
+
});
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
prompt();
|
|
53
222
|
}
|
|
54
223
|
|
|
55
224
|
main().catch(console.error);
|
|
@@ -1,38 +1,48 @@
|
|
|
1
1
|
import 'dotenv/config';
|
|
2
|
+
import * as readline from 'readline';
|
|
3
|
+
import { createCore } from '@acip/core';
|
|
2
4
|
import { ModelInvocationModule, type ModelInvocationConfig } from '@acip/model-invocation';
|
|
3
5
|
|
|
4
6
|
type ProviderConfigWithEnabled = ModelInvocationConfig['providers'][number] & { enabled: boolean };
|
|
7
|
+
type Message = { role: 'system' | 'user' | 'assistant'; content: string };
|
|
8
|
+
|
|
9
|
+
const SYSTEM_PROMPT = `You are ACIP Assistant, an AI powered by the Adaptive Contextual Intelligence Protocol (ACIP). ACIP is a distributed AI communication protocol providing semantic routing, resource-aware scheduling, and edge computing support. Help users understand and build with ACIP.`;
|
|
5
10
|
|
|
6
11
|
async function main(): Promise<void> {
|
|
7
12
|
const apiKey = process.env.OPENAI_API_KEY || process.env.ACIP_API_KEY;
|
|
8
13
|
const baseUrl = process.env.OPENAI_BASE_URL || process.env.ACIP_BASE_URL;
|
|
9
14
|
const modelId = process.env.DEFAULT_MODEL_ID || 'gpt-5.2';
|
|
15
|
+
const requestTimeoutMs = Number(process.env.ACIP_REQUEST_TIMEOUT_MS || '180000');
|
|
10
16
|
|
|
11
17
|
if (!apiKey) {
|
|
12
|
-
|
|
18
|
+
console.error('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
|
|
19
|
+
process.exit(1);
|
|
13
20
|
}
|
|
14
21
|
|
|
22
|
+
// 初始化 ACIP Core
|
|
23
|
+
const core = createCore();
|
|
24
|
+
console.log(`ACIP Core initialized (instance: ${core.instanceId})`);
|
|
25
|
+
|
|
26
|
+
// 配置模型调用
|
|
15
27
|
const provider: ProviderConfigWithEnabled = {
|
|
16
28
|
name: 'openai',
|
|
17
29
|
apiKey,
|
|
18
30
|
baseUrl,
|
|
19
31
|
enabled: true,
|
|
20
|
-
models: [
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
]
|
|
32
|
+
models: [{
|
|
33
|
+
id: modelId,
|
|
34
|
+
name: modelId,
|
|
35
|
+
provider: 'openai',
|
|
36
|
+
capabilities: ['chat'],
|
|
37
|
+
parameters: {}
|
|
38
|
+
}]
|
|
29
39
|
};
|
|
30
40
|
|
|
31
41
|
const config: ModelInvocationConfig = {
|
|
32
42
|
providers: [provider],
|
|
33
43
|
caching: { enabled: false, ttl: 0, maxSize: 0, strategy: 'lru' },
|
|
34
44
|
observability: { enabled: false, metricsEnabled: false, tracingEnabled: false, exportInterval: 0 },
|
|
35
|
-
streaming: { enabled:
|
|
45
|
+
streaming: { enabled: true, chunkSize: 1, timeout: requestTimeoutMs }
|
|
36
46
|
};
|
|
37
47
|
|
|
38
48
|
const modelInvocation = new ModelInvocationModule(config);
|
|
@@ -42,16 +52,174 @@ async function main(): Promise<void> {
|
|
|
42
52
|
modelInvocation.once('error', (error) => reject(error));
|
|
43
53
|
});
|
|
44
54
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
console.log('Model Invocation ready');
|
|
56
|
+
|
|
57
|
+
// 对话历史
|
|
58
|
+
const history: Message[] = [];
|
|
59
|
+
const resolveModelsUrl = (rawBaseUrl?: string): string => {
|
|
60
|
+
const fallback = 'https://api.openai.com/v1';
|
|
61
|
+
const trimmed = (rawBaseUrl || fallback).replace(/\/$/, '');
|
|
62
|
+
if (trimmed.endsWith('/models')) {
|
|
63
|
+
return trimmed;
|
|
64
|
+
}
|
|
65
|
+
if (trimmed.endsWith('/v1')) {
|
|
66
|
+
return `${trimmed}/models`;
|
|
67
|
+
}
|
|
68
|
+
return `${trimmed}/v1/models`;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const listModels = async (): Promise<void> => {
|
|
72
|
+
if (!apiKey) {
|
|
73
|
+
console.log('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
|
|
74
|
+
console.log('');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const modelsUrl = resolveModelsUrl(baseUrl);
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(modelsUrl, {
|
|
81
|
+
method: 'GET',
|
|
82
|
+
headers: {
|
|
83
|
+
'Authorization': `Bearer ${apiKey}`
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
let errorMessage = `HTTP ${response.status}`;
|
|
89
|
+
try {
|
|
90
|
+
const error: any = await response.json();
|
|
91
|
+
errorMessage = error?.error?.message || error?.message || errorMessage;
|
|
92
|
+
} catch {
|
|
93
|
+
const text = await response.text();
|
|
94
|
+
if (text) {
|
|
95
|
+
errorMessage = text;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
console.log(`Models list error: ${errorMessage}`);
|
|
99
|
+
console.log('');
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const data: any = await response.json();
|
|
104
|
+
const models = Array.isArray(data?.data)
|
|
105
|
+
? data.data
|
|
106
|
+
: Array.isArray(data?.models)
|
|
107
|
+
? data.models
|
|
108
|
+
: [];
|
|
109
|
+
|
|
110
|
+
if (models.length === 0) {
|
|
111
|
+
console.log('No models returned.');
|
|
112
|
+
console.log('');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log('');
|
|
117
|
+
console.log(`--- Models (${models.length}) ---`);
|
|
118
|
+
for (const model of models) {
|
|
119
|
+
const id = model?.id || model?.name || model?.model || JSON.stringify(model);
|
|
120
|
+
console.log(` - ${id}`);
|
|
121
|
+
}
|
|
122
|
+
console.log('---');
|
|
123
|
+
console.log('');
|
|
124
|
+
} catch (error: any) {
|
|
125
|
+
console.log(`Models list error: ${error.message}`);
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// 创建 REPL
|
|
131
|
+
const rl = readline.createInterface({
|
|
132
|
+
input: process.stdin,
|
|
133
|
+
output: process.stdout
|
|
52
134
|
});
|
|
53
135
|
|
|
54
|
-
console.log('
|
|
136
|
+
console.log('\n=== ACIP Assistant ===');
|
|
137
|
+
console.log('Type your message to chat with AI.');
|
|
138
|
+
console.log('Commands: /help, /models, /clear, /exit\n');
|
|
139
|
+
|
|
140
|
+
const prompt = (): void => {
|
|
141
|
+
rl.question('You> ', async (input) => {
|
|
142
|
+
const trimmed = input.trim();
|
|
143
|
+
if (!trimmed) { prompt(); return; }
|
|
144
|
+
|
|
145
|
+
if (trimmed === '/exit' || trimmed === '/quit') {
|
|
146
|
+
console.log('Goodbye!');
|
|
147
|
+
rl.close();
|
|
148
|
+
process.exit(0);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (trimmed === '/help') {
|
|
152
|
+
console.log('\nCommands:');
|
|
153
|
+
console.log(' /help - Show this help');
|
|
154
|
+
console.log(' /models - List available models');
|
|
155
|
+
console.log(' /clear - Clear conversation history');
|
|
156
|
+
console.log(' /exit - Exit the application\n');
|
|
157
|
+
prompt();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (trimmed === '/models') {
|
|
162
|
+
await listModels();
|
|
163
|
+
prompt();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (trimmed === '/clear') {
|
|
168
|
+
history.length = 0;
|
|
169
|
+
console.log('Conversation history cleared.\n');
|
|
170
|
+
prompt();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const messages: Message[] = [
|
|
176
|
+
{ role: 'system', content: SYSTEM_PROMPT },
|
|
177
|
+
...history,
|
|
178
|
+
{ role: 'user', content: trimmed }
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
const responseContent = await new Promise<string>((resolve, reject) => {
|
|
182
|
+
const stream = modelInvocation.invokeStream({
|
|
183
|
+
model: modelId,
|
|
184
|
+
provider: 'openai',
|
|
185
|
+
timeout: requestTimeoutMs,
|
|
186
|
+
messages
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
let full = '';
|
|
190
|
+
process.stdout.write('\nAssistant> ');
|
|
191
|
+
|
|
192
|
+
stream.on('data', (chunk: any) => {
|
|
193
|
+
const content = chunk?.output?.content ?? chunk?.content;
|
|
194
|
+
if (typeof content === 'string') {
|
|
195
|
+
full += content;
|
|
196
|
+
process.stdout.write(content);
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
stream.on('end', () => {
|
|
201
|
+
process.stdout.write('\n\n');
|
|
202
|
+
resolve(full);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
stream.on('error', (err: any) => {
|
|
206
|
+
const message = err?.error || err?.message || 'Unknown error';
|
|
207
|
+
process.stdout.write('\n');
|
|
208
|
+
reject(new Error(message));
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
history.push({ role: 'user', content: trimmed });
|
|
213
|
+
history.push({ role: 'assistant', content: responseContent });
|
|
214
|
+
} catch (error: any) {
|
|
215
|
+
console.error(`\nError: ${error.message}\n`);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
prompt();
|
|
219
|
+
});
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
prompt();
|
|
55
223
|
}
|
|
56
224
|
|
|
57
225
|
main().catch(console.error);
|