@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.
@@ -1,65 +1,345 @@
1
- import type { ModelInvocationModule, InvocationResponse, InvocationOptions } from '@acip/model-invocation';
1
+ import * as readline from 'readline';
2
+ import type { ModelInvocationModule } from '@acip/model-invocation';
2
3
 
3
- type AssistantOptions = {
4
+ type Message = { role: 'system' | 'user' | 'assistant'; content: string };
5
+
6
+ export interface AssistantModules {
7
+ modelInvocation: ModelInvocationModule;
4
8
  modelId: string;
5
- provider?: string;
6
- systemPrompt?: string;
7
- };
9
+ provider: string;
10
+ contextManager?: any;
11
+ contextId?: string | null;
12
+ blockchain?: any;
13
+ auditService?: any;
14
+ dataAccess?: any;
15
+ authManager?: any;
16
+ }
8
17
 
9
- type Message = { role: 'system' | 'user' | 'assistant'; content: string };
18
+ const SYSTEM_PROMPT = `You are ACIP Assistant, an AI powered by the Adaptive Contextual Intelligence Protocol (ACIP).
19
+ ACIP is a distributed AI communication protocol providing semantic routing, resource-aware scheduling, QoS management, blockchain integration, and edge computing support.
20
+ You have access to context management, data access, blockchain audit, and security modules.
21
+ Help users understand and build with ACIP. Be concise and helpful.`;
22
+
23
+ export function createAssistant(modules: AssistantModules) {
24
+ const {
25
+ modelInvocation,
26
+ modelId,
27
+ provider,
28
+ contextManager,
29
+ contextId,
30
+ blockchain,
31
+ auditService,
32
+ dataAccess,
33
+ authManager
34
+ } = modules;
10
35
 
11
- /**
12
- * 创建AI助手实例
13
- */
14
- export function createAssistant(
15
- modelInvocation: ModelInvocationModule,
16
- options: AssistantOptions
17
- ) {
18
36
  const history: Message[] = [];
19
- const provider = options.provider || 'openai';
20
- const modelId = options.modelId;
21
- const systemPrompt = options.systemPrompt || `
22
- You are ACIP Guide, an AI assistant specializing in the Adaptive Contextual Intelligence Protocol.
23
- Provide helpful, accurate, and concise information about ACIP's features, architecture, and usage.
24
- When asked about technical details, include code examples when appropriate.
25
- If you don't know something, admit it rather than making up information.
26
- `;
27
-
28
- // 发送消息并返回响应
29
- async function sendMessage(
30
- content: string,
31
- invokeOptions: Omit<InvocationOptions, 'model' | 'provider' | 'messages'> = {}
32
- ): Promise<InvocationResponse> {
37
+ let requestCount = 0;
38
+ const requestTimeoutMs = Number(process.env.ACIP_REQUEST_TIMEOUT_MS || '180000');
39
+
40
+ const resolveModelsUrl = (rawBaseUrl?: string): string => {
41
+ const fallback = 'https://api.openai.com/v1';
42
+ const trimmed = (rawBaseUrl || fallback).replace(/\/$/, '');
43
+ if (trimmed.endsWith('/models')) {
44
+ return trimmed;
45
+ }
46
+ if (trimmed.endsWith('/v1')) {
47
+ return `${trimmed}/models`;
48
+ }
49
+ return `${trimmed}/v1/models`;
50
+ };
51
+
52
+ const listModels = async (): Promise<void> => {
53
+ const apiKey = process.env.OPENAI_API_KEY || process.env.ACIP_API_KEY;
54
+ const baseUrl = process.env.OPENAI_BASE_URL || process.env.ACIP_BASE_URL;
55
+
56
+ if (!apiKey) {
57
+ console.log('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
58
+ console.log('');
59
+ return;
60
+ }
61
+
62
+ const modelsUrl = resolveModelsUrl(baseUrl);
63
+ try {
64
+ const response = await fetch(modelsUrl, {
65
+ method: 'GET',
66
+ headers: {
67
+ 'Authorization': `Bearer ${apiKey}`
68
+ }
69
+ });
70
+
71
+ if (!response.ok) {
72
+ let errorMessage = `HTTP ${response.status}`;
73
+ try {
74
+ const error: any = await response.json();
75
+ errorMessage = error?.error?.message || error?.message || errorMessage;
76
+ } catch {
77
+ const text = await response.text();
78
+ if (text) {
79
+ errorMessage = text;
80
+ }
81
+ }
82
+ console.log(`Models list error: ${errorMessage}`);
83
+ console.log('');
84
+ return;
85
+ }
86
+
87
+ const data: any = await response.json();
88
+ const models = Array.isArray(data?.data)
89
+ ? data.data
90
+ : Array.isArray(data?.models)
91
+ ? data.models
92
+ : [];
93
+
94
+ if (models.length === 0) {
95
+ console.log('No models returned.');
96
+ console.log('');
97
+ return;
98
+ }
99
+
100
+ console.log('');
101
+ console.log(`--- Models (${models.length}) ---`);
102
+ for (const model of models) {
103
+ const id = model?.id || model?.name || model?.model || JSON.stringify(model);
104
+ console.log(` - ${id}`);
105
+ }
106
+ console.log('---');
107
+ console.log('');
108
+ } catch (error: any) {
109
+ console.log(`Models list error: ${error.message}`);
110
+ console.log('');
111
+ }
112
+ };
113
+
114
+ const streamReply = (messages: Message[]): Promise<string> => new Promise((resolve, reject) => {
115
+ const stream = modelInvocation.invokeStream({
116
+ model: modelId,
117
+ provider,
118
+ timeout: requestTimeoutMs,
119
+ messages
120
+ });
121
+
122
+ let full = '';
123
+ process.stdout.write('\nAssistant> ');
124
+
125
+ stream.on('data', (chunk: any) => {
126
+
127
+ const content = chunk?.output?.content ?? chunk?.content;
128
+
129
+ if (typeof content === 'string') {
130
+
131
+ full += content;
132
+
133
+ process.stdout.write(content);
134
+
135
+ }
136
+
137
+ });
138
+
139
+ stream.on('end', () => {
140
+ process.stdout.write('\n\n');
141
+ resolve(full);
142
+ });
143
+
144
+ stream.on('error', (err: any) => {
145
+ const message = err?.error || err?.message || 'Unknown error';
146
+ process.stdout.write('\n');
147
+ reject(new Error(message));
148
+ });
149
+ });
150
+
151
+
152
+ async function sendMessage(content: string): Promise<string> {
33
153
  const messages: Message[] = [
34
- { role: 'system', content: systemPrompt.trim() },
154
+ { role: 'system', content: SYSTEM_PROMPT },
35
155
  ...history,
36
156
  { role: 'user', content }
37
157
  ];
38
158
 
39
- const response = await modelInvocation.invoke({
40
- model: modelId,
41
- provider,
42
- messages,
43
- ...invokeOptions
44
- });
159
+ const responseContent = await streamReply(messages);
45
160
 
46
161
  history.push({ role: 'user', content });
47
- history.push({ role: 'assistant', content: response.content });
162
+ history.push({ role: 'assistant', content: responseContent });
163
+ requestCount++;
164
+
165
+ // 自动保存到上下文管理
166
+ if (contextManager && contextId) {
167
+ try {
168
+ await contextManager.addToContext(contextId, {
169
+ type: 'text',
170
+ text: `User: ${content}\nAssistant: ${responseContent}`,
171
+ timestamp: Date.now()
172
+ });
173
+ } catch { /* 静默降级 */ }
174
+ }
175
+
176
+ // 自动审计到区块链
177
+ if (blockchain) {
178
+ try {
179
+ const tx = blockchain.createTransaction('user', 'ai-assistant', 1);
180
+ tx.type = 'CONTRIBUTION';
181
+ blockchain.addTransaction(tx);
182
+ } catch { /* 静默降级 */ }
183
+ }
48
184
 
49
- return response;
185
+ return responseContent;
50
186
  }
51
187
 
52
- function getHistory(): Message[] {
53
- return [...history];
188
+ async function handleCommand(cmd: string): Promise<boolean> {
189
+ const parts = cmd.split(/\s+/);
190
+ const command = parts[0];
191
+
192
+ switch (command) {
193
+ case '/help':
194
+ console.log('\nCommands:');
195
+ console.log(' /help - Show this help');
196
+ console.log(' /clear - Clear conversation history');
197
+ console.log(' /history - Show conversation history');
198
+ console.log(' /status - Show module status');
199
+ console.log(' /models - List available models');
200
+ console.log(' /audit - Show blockchain audit trail');
201
+ console.log(' /data list - List registered data sources');
202
+ console.log(' /exit - Exit the application\n');
203
+ return true;
204
+
205
+ case '/clear':
206
+ history.length = 0;
207
+ requestCount = 0;
208
+ console.log('Conversation history cleared.\n');
209
+ return true;
210
+
211
+ case '/history':
212
+ if (history.length === 0) {
213
+ console.log('No conversation history yet.\n');
214
+ } else {
215
+ console.log(`\n--- Conversation History (${history.length} messages) ---`);
216
+ for (const msg of history) {
217
+ const prefix = msg.role === 'user' ? 'You' : 'Assistant';
218
+ const text = msg.content.length > 120
219
+ ? msg.content.substring(0, 120) + '...'
220
+ : msg.content;
221
+ console.log(` [${prefix}] ${text}`);
222
+ }
223
+ console.log('---\n');
224
+ }
225
+ return true;
226
+
227
+ case '/status': {
228
+ console.log('\n--- Module Status ---');
229
+ console.log(` Core: active`);
230
+ console.log(` Model Invocation: active (model: ${modelId})`);
231
+ console.log(` Context Mgmt: ${contextManager ? 'active' : 'unavailable'}`);
232
+ console.log(` Data Access: ${dataAccess ? 'active' : 'unavailable'}`);
233
+ console.log(` Blockchain: ${blockchain ? 'active' : 'unavailable'}`);
234
+ console.log(` Security/Auth: ${authManager ? 'active' : 'unavailable'}`);
235
+ console.log(` Requests: ${requestCount}`);
236
+ console.log(` History: ${history.length} messages`);
237
+ if (authManager) {
238
+ try {
239
+ const stats = authManager.getStats();
240
+ console.log(` Users: ${stats.userCount}`);
241
+ console.log(` Sessions: ${stats.sessionCount}`);
242
+ } catch { /* ignore */ }
243
+ }
244
+ console.log('---\n');
245
+ return true;
246
+ }
247
+
248
+ case '/models':
249
+ await listModels();
250
+ return true;
251
+
252
+ case '/audit':
253
+ if (!auditService) {
254
+ console.log('Blockchain audit is not available.\n');
255
+ } else {
256
+ try {
257
+ const trail = auditService.getAuditTrailForAddress('user');
258
+ if (trail.length === 0) {
259
+ console.log('No audit records yet.\n');
260
+ } else {
261
+ console.log(`\n--- Audit Trail (${trail.length} records) ---`);
262
+ for (const tx of trail.slice(-10)) {
263
+ const time = new Date(tx.timestamp).toLocaleTimeString();
264
+ console.log(` [${time}] ${tx.from} -> ${tx.to} (${tx.type}, amount: ${tx.amount})`);
265
+ }
266
+ console.log('---\n');
267
+ }
268
+ } catch (err: any) {
269
+ console.log(`Audit error: ${err.message}\n`);
270
+ }
271
+ }
272
+ return true;
273
+
274
+ case '/data':
275
+ if (!dataAccess) {
276
+ console.log('Data Access module is not available.\n');
277
+ } else if (parts[1] === 'list') {
278
+ try {
279
+ const sources = dataAccess.listSources();
280
+ if (sources.length === 0) {
281
+ console.log('No data sources registered.\n');
282
+ } else {
283
+ console.log(`\n--- Data Sources (${sources.length}) ---`);
284
+ for (const src of sources) {
285
+ console.log(` - ${src.name || src}`);
286
+ }
287
+ console.log('---\n');
288
+ }
289
+ } catch (err: any) {
290
+ console.log(`Data access error: ${err.message}\n`);
291
+ }
292
+ } else {
293
+ console.log('Usage: /data list\n');
294
+ }
295
+ return true;
296
+
297
+ case '/exit':
298
+ case '/quit':
299
+ console.log('Goodbye!');
300
+ process.exit(0);
301
+
302
+ default:
303
+ return false;
304
+ }
54
305
  }
55
306
 
56
- function clearHistory(): void {
57
- history.length = 0;
307
+ function startREPL(): void {
308
+ const rl = readline.createInterface({
309
+ input: process.stdin,
310
+ output: process.stdout
311
+ });
312
+
313
+ console.log('\n=== ACIP Advanced Assistant ===');
314
+ console.log('All modules integrated. Type /help for commands.\n');
315
+
316
+ const prompt = (): void => {
317
+ rl.question('You> ', async (input) => {
318
+ const trimmed = input.trim();
319
+ if (!trimmed) { prompt(); return; }
320
+
321
+ if (trimmed.startsWith('/')) {
322
+ const handled = await handleCommand(trimmed);
323
+ if (handled) { prompt(); return; }
324
+ }
325
+
326
+ try {
327
+ await sendMessage(trimmed);
328
+ } catch (error: any) {
329
+ console.error(`\nError: ${error.message}\n`);
330
+ }
331
+
332
+ prompt();
333
+ });
334
+ };
335
+
336
+ prompt();
58
337
  }
59
338
 
60
339
  return {
340
+ startREPL,
61
341
  sendMessage,
62
- getHistory,
63
- clearHistory
342
+ getHistory: (): Message[] => [...history],
343
+ clearHistory: (): void => { history.length = 0; }
64
344
  };
65
345
  }
@@ -1,4 +1,5 @@
1
1
  import 'dotenv/config';
2
+ import { createCore } from '@acip/core';
2
3
  import { ModelInvocationModule } from '@acip/model-invocation';
3
4
  import { createAssistant } from './components/assistant.js';
4
5
 
@@ -6,62 +7,144 @@ async function main() {
6
7
  const apiKey = process.env.OPENAI_API_KEY || process.env.ACIP_API_KEY;
7
8
  const baseUrl = process.env.OPENAI_BASE_URL || process.env.ACIP_BASE_URL;
8
9
  const modelId = process.env.DEFAULT_MODEL_ID || 'gpt-5.2';
10
+ const requestTimeoutMs = Number(process.env.ACIP_REQUEST_TIMEOUT_MS || '180000');
9
11
 
10
12
  if (!apiKey) {
11
- throw new Error('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY).');
13
+ console.error('Missing API key. Set OPENAI_API_KEY (or ACIP_API_KEY) in .env file.');
14
+ process.exit(1);
12
15
  }
13
16
 
17
+ // ── 1. ACIP Core ──
18
+ const core = createCore();
19
+ console.log(`[core] initialized (instance: ${core.instanceId})`);
20
+
21
+ // ── 2. Model Invocation ──
14
22
  const provider = {
15
23
  name: 'openai',
16
24
  apiKey,
17
25
  baseUrl,
18
26
  enabled: true,
19
- models: [
20
- {
21
- id: modelId,
22
- name: modelId,
23
- provider: 'openai',
24
- capabilities: ['chat'],
25
- parameters: {}
26
- }
27
- ]
27
+ models: [{
28
+ id: modelId,
29
+ name: modelId,
30
+ provider: 'openai',
31
+ capabilities: ['chat'],
32
+ parameters: {}
33
+ }]
28
34
  };
29
35
 
30
- const config = {
36
+ const miConfig = {
31
37
  providers: [provider],
32
38
  caching: { enabled: true, ttl: 600, maxSize: 100, strategy: 'lru' },
33
39
  observability: { enabled: true, metricsEnabled: true, tracingEnabled: false, exportInterval: 60000 },
34
- streaming: { enabled: false, chunkSize: 1, timeout: 60000 }
40
+ streaming: { enabled: true, chunkSize: 1, timeout: requestTimeoutMs }
35
41
  };
36
42
 
37
- const modelInvocation = new ModelInvocationModule(config);
43
+ const modelInvocation = new ModelInvocationModule(miConfig);
38
44
 
39
45
  await new Promise((resolve, reject) => {
40
46
  modelInvocation.once('ready', () => resolve());
41
47
  modelInvocation.once('error', (error) => reject(error));
42
48
  });
43
49
 
44
- modelInvocation.on('requestStarted', (data) => {
45
- console.log(`Model invocation started: ${data.requestId}`);
46
- });
50
+ console.log('[model-invocation] ready');
47
51
 
48
- modelInvocation.on('requestCompleted', (data) => {
49
- console.log(`Model invocation completed in ${data.duration}ms`);
50
- });
52
+ // ── 3. Context Management (降级运行) ──
53
+ let contextManager = null;
54
+ let contextId = null;
55
+ try {
56
+ const { ContextManager } = await import('@acip/context-management');
57
+ contextManager = new ContextManager(
58
+ core.instanceId,
59
+ core.stateManager,
60
+ core.telemetryService,
61
+ core.eventManager
62
+ );
63
+ await contextManager.initialize();
64
+ await contextManager.createContext('main-conversation');
65
+ contextId = 'main-conversation';
66
+ console.log('[context-management] ready');
67
+ } catch (err) {
68
+ console.warn(`[context-management] skipped: ${err.message}`);
69
+ }
51
70
 
52
- const assistant = createAssistant(modelInvocation, {
53
- modelId,
54
- provider: 'openai'
55
- });
71
+ // ── 4. Data Access (降级运行) ──
72
+ let dataAccess = null;
73
+ try {
74
+ const { DataAccessModule, InMemoryAdapter } = await import('@acip/data-access');
75
+ dataAccess = new DataAccessModule();
76
+ const adapter = new InMemoryAdapter();
77
+ await dataAccess.registerSource('notes', { adapter });
78
+ console.log('[data-access] ready');
79
+ } catch (err) {
80
+ console.warn(`[data-access] skipped: ${err.message}`);
81
+ }
56
82
 
57
- const response = await assistant.sendMessage(
58
- 'Hello! I want to learn about adaptive context management in AI systems.'
59
- );
83
+ // ── 5. Blockchain + Audit (降级运行) ──
84
+ let blockchain = null;
85
+ let auditService = null;
86
+ try {
87
+ const { Blockchain, AuditService } = await import('@acip/blockchain');
88
+ blockchain = new Blockchain();
89
+ auditService = new AuditService(blockchain);
90
+ console.log('[blockchain] ready');
91
+ } catch (err) {
92
+ console.warn(`[blockchain] skipped: ${err.message}`);
93
+ }
94
+
95
+ // ── 6. Security & Authentication (降级运行) ──
96
+ let authManager = null;
97
+ try {
98
+ const { AuthManager } = await import('@acip/security-authentication');
99
+ authManager = new AuthManager({
100
+ passwordPolicy: {
101
+ minLength: 4,
102
+ requireNumbers: false,
103
+ requireLowercase: false,
104
+ requireUppercase: false,
105
+ requireSpecial: false,
106
+ maxRepeatingChars: 0,
107
+ preventCommonPasswords: false
108
+ }
109
+ });
110
+ await authManager.initialize({
111
+ jwtSecret: process.env.JWT_SECRET || 'acip-demo-secret'
112
+ });
113
+ await authManager.start();
114
+
115
+ const adminPassword = process.env.ADMIN_PASSWORD || 'admin';
116
+ try {
117
+ await authManager.registerUser('admin', {
118
+ password: adminPassword,
119
+ roles: ['admin']
120
+ });
121
+ const auth = await authManager.authenticate('admin', adminPassword);
122
+ if (auth.authenticated) {
123
+ console.log('[security] ready — admin authenticated');
124
+ }
125
+ } catch {
126
+ // 用户可能已存在
127
+ }
128
+ console.log('[security-authentication] ready');
129
+ } catch (err) {
130
+ console.warn(`[security-authentication] skipped: ${err.message}`);
131
+ }
60
132
 
61
- console.log('Assistant Response:', response.content);
133
+ // ── 启动交互式助手 ──
134
+ const modules = {
135
+ modelInvocation,
136
+ modelId,
137
+ provider: 'openai',
138
+ contextManager,
139
+ contextId,
140
+ blockchain,
141
+ auditService,
142
+ dataAccess,
143
+ authManager
144
+ };
62
145
 
63
- const history = assistant.getHistory();
64
- console.log(`Conversation has ${history.length} messages`);
146
+ const assistant = createAssistant(modules);
147
+ assistant.startREPL();
65
148
  }
66
149
 
67
150
  main().catch(console.error);