adonis-ai-kit 0.0.0-stage → 1.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/LICENSE +21 -0
- package/README.md +325 -2
- package/dist/commands/ai_test.d.ts +12 -0
- package/dist/commands/ai_test.d.ts.map +1 -0
- package/dist/commands/ai_test.js +98 -0
- package/dist/commands/ai_test.js.map +1 -0
- package/dist/commands/main.d.ts +14 -0
- package/dist/commands/main.d.ts.map +1 -0
- package/dist/commands/main.js +31 -0
- package/dist/commands/main.js.map +1 -0
- package/dist/commands/make_agent.d.ts +13 -0
- package/dist/commands/make_agent.d.ts.map +1 -0
- package/dist/commands/make_agent.js +116 -0
- package/dist/commands/make_agent.js.map +1 -0
- package/dist/providers/ai_provider.d.ts +23 -0
- package/dist/providers/ai_provider.d.ts.map +1 -0
- package/dist/providers/ai_provider.js +34 -0
- package/dist/providers/ai_provider.js.map +1 -0
- package/dist/services/main.d.ts +4 -0
- package/dist/services/main.d.ts.map +1 -0
- package/dist/services/main.js +7 -0
- package/dist/services/main.js.map +1 -0
- package/dist/src/acp/acp_agents.d.ts +75 -0
- package/dist/src/acp/acp_agents.d.ts.map +1 -0
- package/dist/src/acp/acp_agents.js +343 -0
- package/dist/src/acp/acp_agents.js.map +1 -0
- package/dist/src/acp/index.d.ts +4 -0
- package/dist/src/acp/index.d.ts.map +1 -0
- package/dist/src/acp/index.js +4 -0
- package/dist/src/acp/index.js.map +1 -0
- package/dist/src/acp/provider.d.ts +64 -0
- package/dist/src/acp/provider.d.ts.map +1 -0
- package/dist/src/acp/provider.js +255 -0
- package/dist/src/acp/provider.js.map +1 -0
- package/dist/src/acp/types.d.ts +107 -0
- package/dist/src/acp/types.d.ts.map +1 -0
- package/dist/src/acp/types.js +2 -0
- package/dist/src/acp/types.js.map +1 -0
- package/dist/src/agents/agent_runner.d.ts +60 -0
- package/dist/src/agents/agent_runner.d.ts.map +1 -0
- package/dist/src/agents/agent_runner.js +134 -0
- package/dist/src/agents/agent_runner.js.map +1 -0
- package/dist/src/agents/index.d.ts +4 -0
- package/dist/src/agents/index.d.ts.map +1 -0
- package/dist/src/agents/index.js +4 -0
- package/dist/src/agents/index.js.map +1 -0
- package/dist/src/agents/loader.d.ts +56 -0
- package/dist/src/agents/loader.d.ts.map +1 -0
- package/dist/src/agents/loader.js +184 -0
- package/dist/src/agents/loader.js.map +1 -0
- package/dist/src/agents/manifest.d.ts +61 -0
- package/dist/src/agents/manifest.d.ts.map +1 -0
- package/dist/src/agents/manifest.js +35 -0
- package/dist/src/agents/manifest.js.map +1 -0
- package/dist/src/ai_manager.d.ts +136 -0
- package/dist/src/ai_manager.d.ts.map +1 -0
- package/dist/src/ai_manager.js +701 -0
- package/dist/src/ai_manager.js.map +1 -0
- package/dist/src/budget/budget_allocator.d.ts +37 -0
- package/dist/src/budget/budget_allocator.d.ts.map +1 -0
- package/dist/src/budget/budget_allocator.js +98 -0
- package/dist/src/budget/budget_allocator.js.map +1 -0
- package/dist/src/budget/budget_enforcer.d.ts +70 -0
- package/dist/src/budget/budget_enforcer.d.ts.map +1 -0
- package/dist/src/budget/budget_enforcer.js +195 -0
- package/dist/src/budget/budget_enforcer.js.map +1 -0
- package/dist/src/budget/cost_tracker.d.ts +71 -0
- package/dist/src/budget/cost_tracker.d.ts.map +1 -0
- package/dist/src/budget/cost_tracker.js +199 -0
- package/dist/src/budget/cost_tracker.js.map +1 -0
- package/dist/src/budget/index.d.ts +6 -0
- package/dist/src/budget/index.d.ts.map +1 -0
- package/dist/src/budget/index.js +6 -0
- package/dist/src/budget/index.js.map +1 -0
- package/dist/src/budget/pricing.d.ts +35 -0
- package/dist/src/budget/pricing.d.ts.map +1 -0
- package/dist/src/budget/pricing.js +129 -0
- package/dist/src/budget/pricing.js.map +1 -0
- package/dist/src/budget/types.d.ts +91 -0
- package/dist/src/budget/types.d.ts.map +1 -0
- package/dist/src/budget/types.js +2 -0
- package/dist/src/budget/types.js.map +1 -0
- package/dist/src/catalog/index.d.ts +3 -0
- package/dist/src/catalog/index.d.ts.map +1 -0
- package/dist/src/catalog/index.js +3 -0
- package/dist/src/catalog/index.js.map +1 -0
- package/dist/src/catalog/models_dev.d.ts +87 -0
- package/dist/src/catalog/models_dev.d.ts.map +1 -0
- package/dist/src/catalog/models_dev.js +38 -0
- package/dist/src/catalog/models_dev.js.map +1 -0
- package/dist/src/catalog/source.d.ts +52 -0
- package/dist/src/catalog/source.d.ts.map +1 -0
- package/dist/src/catalog/source.js +668 -0
- package/dist/src/catalog/source.js.map +1 -0
- package/dist/src/circuit_breaker.d.ts +40 -0
- package/dist/src/circuit_breaker.d.ts.map +1 -0
- package/dist/src/circuit_breaker.js +172 -0
- package/dist/src/circuit_breaker.js.map +1 -0
- package/dist/src/define_config.d.ts +11 -0
- package/dist/src/define_config.d.ts.map +1 -0
- package/dist/src/define_config.js +48 -0
- package/dist/src/define_config.js.map +1 -0
- package/dist/src/error_classifier.d.ts +17 -0
- package/dist/src/error_classifier.d.ts.map +1 -0
- package/dist/src/error_classifier.js +80 -0
- package/dist/src/error_classifier.js.map +1 -0
- package/dist/src/errors.d.ts +52 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.js +70 -0
- package/dist/src/errors.js.map +1 -0
- package/dist/src/guard/confirmation.d.ts +67 -0
- package/dist/src/guard/confirmation.d.ts.map +1 -0
- package/dist/src/guard/confirmation.js +180 -0
- package/dist/src/guard/confirmation.js.map +1 -0
- package/dist/src/guard/errors.d.ts +10 -0
- package/dist/src/guard/errors.d.ts.map +1 -0
- package/dist/src/guard/errors.js +14 -0
- package/dist/src/guard/errors.js.map +1 -0
- package/dist/src/guard/guard.d.ts +47 -0
- package/dist/src/guard/guard.d.ts.map +1 -0
- package/dist/src/guard/guard.js +133 -0
- package/dist/src/guard/guard.js.map +1 -0
- package/dist/src/guard/index.d.ts +7 -0
- package/dist/src/guard/index.d.ts.map +1 -0
- package/dist/src/guard/index.js +7 -0
- package/dist/src/guard/index.js.map +1 -0
- package/dist/src/guard/registry.d.ts +66 -0
- package/dist/src/guard/registry.d.ts.map +1 -0
- package/dist/src/guard/registry.js +182 -0
- package/dist/src/guard/registry.js.map +1 -0
- package/dist/src/guard/schema_registry.d.ts +37 -0
- package/dist/src/guard/schema_registry.d.ts.map +1 -0
- package/dist/src/guard/schema_registry.js +57 -0
- package/dist/src/guard/schema_registry.js.map +1 -0
- package/dist/src/guard/types.d.ts +64 -0
- package/dist/src/guard/types.d.ts.map +1 -0
- package/dist/src/guard/types.js +2 -0
- package/dist/src/guard/types.js.map +1 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +12 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/response_macro.d.ts +16 -0
- package/dist/src/response_macro.d.ts.map +1 -0
- package/dist/src/response_macro.js +151 -0
- package/dist/src/response_macro.js.map +1 -0
- package/dist/src/types.d.ts +297 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/usage.d.ts +13 -0
- package/dist/src/usage.d.ts.map +1 -0
- package/dist/src/usage.js +61 -0
- package/dist/src/usage.js.map +1 -0
- package/package.json +141 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 IvanGlazyrin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,326 @@
|
|
|
1
|
-
#
|
|
1
|
+
# adonis-ai
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **Production-grade AI & Agentic workflow toolkit for AdonisJS 7**, powered by Vercel AI SDK v7, multi-provider cascading fallback, circuit breakers, tool security guardrails, token budget tracking, models.dev live catalog, and ACP (Agent Client Protocol) coding agents.
|
|
4
|
+
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://www.npmjs.com/package/adonis-ai-kit)
|
|
7
|
+
[](https://adonisjs.com)
|
|
8
|
+
[](https://www.typescriptlang.org)
|
|
9
|
+
[](https://sdk.vercel.ai)
|
|
10
|
+
[](https://nodejs.org)
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Highlights & Architectural Comparison
|
|
15
|
+
|
|
16
|
+
While frameworks like **Laravel AI** and **Prism** bring basic prompt-level abstractions to PHP, enterprise production systems require strict SRE resilience, tool execution guardrails, cost guarantees, and streaming protocol integrity. **`adonis-ai`** is built from the ground up for high-concurrency Node.js microservices:
|
|
17
|
+
|
|
18
|
+
| Feature | `adonis-ai` (AdonisJS 7) | Laravel AI / Prism (PHP) |
|
|
19
|
+
| :--- | :--- | :--- |
|
|
20
|
+
| **Foundation Engine** | **Vercel AI SDK v7** (native Node.js async streams & tool calls) | Custom cURL/Guzzle wrappers |
|
|
21
|
+
| **Circuit Breaker** | **Per `provider:model` key** (isolated half-open state, probe limits) | Global or none |
|
|
22
|
+
| **Cascading Fallbacks** | **Safe**: never replays side-effects if tool calls already started | Retries from scratch (risk of double actions) |
|
|
23
|
+
| **Tool Security (Guardrails)** | **Fail-closed** with Human-in-the-Loop (HITL), concurrent slots | Basic parameter mapping |
|
|
24
|
+
| **Token Budget & Cost** | **Live USD pricing**, ring-buffer memory, cache-read & reasoning tokens | Raw token count or static tables |
|
|
25
|
+
| **Catalog Auto-Discovery** | **models.dev integration** with TTL cache & atomic writes | Static configuration files |
|
|
26
|
+
| **Agent Protocols** | **ACP (Agent Client Protocol)** for Claude Code, opencode & subagents | Basic prompt templates |
|
|
27
|
+
| **HTTP Streaming** | **`response.streamAI()` macro** with heartbeats, SSE frames & client-abort hook | PHP chunked response buffers |
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Architecture Overview
|
|
32
|
+
|
|
33
|
+
```mermaid
|
|
34
|
+
flowchart TD
|
|
35
|
+
UserApp["AdonisJS Controller / Service"] --> AiManager["AiManager (Singleton Service)"]
|
|
36
|
+
|
|
37
|
+
subgraph SRE_Resilience ["SRE Resilience & Fallback Layer"]
|
|
38
|
+
AiManager --> CircuitBreaker["Circuit Breaker (provider:model)"]
|
|
39
|
+
CircuitBreaker --> FallbackCascade["Cascading Provider Fallback"]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
subgraph Execution_Layer ["Execution & Agent Layer"]
|
|
43
|
+
FallbackCascade --> Providers["LLM Drivers (OpenAI, Anthropic, Google, Groq, ACP)"]
|
|
44
|
+
FallbackCascade --> AgentRunner["AgentRunner (Manifest + Skills)"]
|
|
45
|
+
AgentRunner --> ToolRegistry["Tool Registry (PolicyGuard)"]
|
|
46
|
+
ToolRegistry --> HITL["ConfirmationService (Human-in-the-Loop)"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
subgraph Observability_Cost ["Observability & Governance"]
|
|
50
|
+
Providers --> CostTracker["CostTracker (Ring Buffer)"]
|
|
51
|
+
Providers --> BudgetEnforcer["BudgetEnforcer (Atomic Reservations)"]
|
|
52
|
+
AiManager -.-> ModelsDev["models.dev Catalog (Live Pricing & Limits)"]
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
subgraph Transport_Layer ["Transport Layer"]
|
|
56
|
+
AiManager --> SSE["response.streamAI() Macro (SSE / Heartbeats / Abort)"]
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## Installation
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install adonis-ai-kit
|
|
66
|
+
# or
|
|
67
|
+
pnpm add adonis-ai-kit
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
> Package name on npm is `adonis-ai-kit` (the `adonis-ai` name on npm is taken by another project).
|
|
71
|
+
> The source repository is [github.com/IvanGlazyrin/adonis-ai-kit](https://github.com/IvanGlazyrin/adonis-ai-kit).
|
|
72
|
+
|
|
73
|
+
Add the provider to `adonisrc.ts`:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
export default defineConfig({
|
|
77
|
+
providers: [
|
|
78
|
+
// ...
|
|
79
|
+
() => import('adonis-ai-kit/providers/ai_provider')
|
|
80
|
+
]
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Configure your providers in `config/ai.ts`:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import env from '#start/env'
|
|
88
|
+
import { defineConfig } from 'adonis-ai-kit'
|
|
89
|
+
|
|
90
|
+
export default defineConfig({
|
|
91
|
+
default: 'openai',
|
|
92
|
+
providers: {
|
|
93
|
+
openai: {
|
|
94
|
+
driver: 'openai',
|
|
95
|
+
apiKey: env.get('OPENAI_API_KEY'),
|
|
96
|
+
defaultModel: 'gpt-4o',
|
|
97
|
+
},
|
|
98
|
+
anthropic: {
|
|
99
|
+
driver: 'anthropic',
|
|
100
|
+
apiKey: env.get('ANTHROPIC_API_KEY'),
|
|
101
|
+
defaultModel: 'claude-3-7-sonnet-20250219',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
// Automatically fallback to Anthropic if OpenAI is throttled (429) or down (5xx)
|
|
105
|
+
fallbackCascade: ['anthropic'],
|
|
106
|
+
circuitBreaker: {
|
|
107
|
+
failureThreshold: 5,
|
|
108
|
+
recoveryTimeoutMs: 30_000,
|
|
109
|
+
halfOpenMaxAttempts: 2,
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Core Capabilities
|
|
117
|
+
|
|
118
|
+
### 1. Basic Generation & Multi-Provider Fallback
|
|
119
|
+
|
|
120
|
+
Generate text with automatic token usage tracking and USD calculation:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import ai from 'adonis-ai-kit/services/main'
|
|
124
|
+
|
|
125
|
+
const result = await ai.generateText({
|
|
126
|
+
prompt: 'Explain vector indexing in 3 sentences',
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
console.log(result.text)
|
|
130
|
+
console.log(`Duration: ${result.durationMs}ms, Cost: $${result.costUsd}`)
|
|
131
|
+
console.log(`Tokens: in=${result.usage.inputTokens}, out=${result.usage.outputTokens}, cached=${result.usage.cacheReadTokens}`)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
If OpenAI returns `429 Rate Limit` or `503 Service Unavailable`, `adonis-ai` automatically routes the request to `anthropic` with its configured model, recording circuit breaker metrics.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
### 2. Structured Output with Zod Schema
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { z } from 'zod'
|
|
142
|
+
import ai from 'adonis-ai-kit/services/main'
|
|
143
|
+
|
|
144
|
+
const resumeSchema = z.object({
|
|
145
|
+
fullName: z.string(),
|
|
146
|
+
skills: z.array(z.string()),
|
|
147
|
+
yearsOfExperience: z.number(),
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const { object } = await ai.generateObject({
|
|
151
|
+
prompt: 'Extract candidate details: Ivan Glazyrin, 9 years in TS and Go',
|
|
152
|
+
schema: resumeSchema,
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
console.log(object.skills) // ['TypeScript', 'Go', ...]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### 3. Server-Sent Events (SSE) Streaming in AdonisJS Controllers
|
|
161
|
+
|
|
162
|
+
Use the built-in `response.streamAI()` macro:
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
import type { HttpContext } from '@adonisjs/core/http'
|
|
166
|
+
import ai from 'adonis-ai-kit/services/main'
|
|
167
|
+
|
|
168
|
+
export default class ChatController {
|
|
169
|
+
async stream({ request, response }: HttpContext) {
|
|
170
|
+
const stream = await ai.streamText({
|
|
171
|
+
prompt: request.input('message'),
|
|
172
|
+
model: 'gpt-4o',
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Sets 'Content-Type: text/event-stream', sends heartbeat pings,
|
|
176
|
+
// and automatically calls stream.abort() if the client disconnects!
|
|
177
|
+
return response.streamAI(stream, {
|
|
178
|
+
heartbeatMs: 15_000,
|
|
179
|
+
mode: 'sse',
|
|
180
|
+
})
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### 4. Tool Security Guardrails & Human-in-the-Loop (HITL)
|
|
188
|
+
|
|
189
|
+
Prevent untrusted AI models from executing destructive operations:
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { ToolRegistry, ConfirmationService, PolicyGuard } from 'adonis-ai-kit/guard'
|
|
193
|
+
import { tool } from 'ai'
|
|
194
|
+
import { z } from 'zod'
|
|
195
|
+
|
|
196
|
+
const confirmationService = new ConfirmationService({ timeoutMs: 60_000 })
|
|
197
|
+
const registry = new ToolRegistry(confirmationService)
|
|
198
|
+
|
|
199
|
+
// Define tools
|
|
200
|
+
registry.register(
|
|
201
|
+
'deploy_service',
|
|
202
|
+
tool({
|
|
203
|
+
description: 'Deploy service to cluster',
|
|
204
|
+
parameters: z.object({ cluster: z.string(), service: z.string() }),
|
|
205
|
+
execute: async ({ cluster, service }) => `Deployed ${service} to ${cluster}`,
|
|
206
|
+
}),
|
|
207
|
+
{ permission: 'ask', maxCallsPerSession: 1 } // Requires human confirmation!
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
// When the model calls 'deploy_service', confirmationService pauses execution,
|
|
211
|
+
// emits a confirmation request event, and resumes only when approved!
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
### 5. Cost Tracking & Atomic Token Budget Enforcement
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { CostTracker, BudgetEnforcer } from 'adonis-ai-kit/budget'
|
|
220
|
+
|
|
221
|
+
const tracker = new CostTracker({ maxRecords: 10_000 })
|
|
222
|
+
const enforcer = new BudgetEnforcer({
|
|
223
|
+
maxCostUsd: 100.0,
|
|
224
|
+
maxInputTokens: 5_000_000,
|
|
225
|
+
maxOutputTokens: 1_000_000,
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
// Atomic pre-flight reservation prevents concurrent race conditions
|
|
229
|
+
const reservation = enforcer.reserve('user-session-42', { estimatedCostUsd: 0.05 })
|
|
230
|
+
if (!reservation.allowed) {
|
|
231
|
+
throw new Error('Budget exceeded')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
// run LLM request...
|
|
236
|
+
enforcer.settle(reservation.reservationId, { costUsd: 0.038 })
|
|
237
|
+
} catch (e) {
|
|
238
|
+
enforcer.release(reservation.reservationId)
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
### 6. ACP (Agent Client Protocol) Coding Agents
|
|
245
|
+
|
|
246
|
+
Run subagents using local CLI tools like **Claude Code**, **opencode**, or custom ACP binaries with sanitized environments:
|
|
247
|
+
|
|
248
|
+
```ts
|
|
249
|
+
import { AgentRunner, AgentLoader } from 'adonis-ai-kit/agents'
|
|
250
|
+
|
|
251
|
+
const loader = new AgentLoader({
|
|
252
|
+
configsDir: './resources/agent-configs',
|
|
253
|
+
skillsDir: './resources/agent-skills',
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
const runner = new AgentRunner(aiManager, registry, loader)
|
|
257
|
+
const execution = await runner.run({
|
|
258
|
+
agentId: 'code-reviewer',
|
|
259
|
+
userPrompt: 'Audit packages/adonis-ai for race conditions',
|
|
260
|
+
})
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Ace CLI Commands
|
|
266
|
+
|
|
267
|
+
`adonis-ai` registers helpful Ace commands:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
# Test connectivity, latency, and model availability across configured providers
|
|
271
|
+
node ace ai:test
|
|
272
|
+
|
|
273
|
+
# Scaffold a new production agent prompt and schema
|
|
274
|
+
node ace make:ai-agent backend-architect --mode=primary
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Testing & Quality
|
|
280
|
+
|
|
281
|
+
The package includes a comprehensive regression test suite with 100% pass rate:
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
npm run test:regression
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
- **Token Budget**: Rate calculations, cache-read discounts, reasoning token surcharges, ring-buffer bounded memory.
|
|
288
|
+
- **Tool Guard**: Policy enforcement (`allow`, `ask`, `deny`), concurrency slots, fail-closed handlers, session validation.
|
|
289
|
+
- **Adonis AI Core**: Cascading failovers without duplicate side-effects, SSE keepalive frames, models.dev live discovery & TTL, ACP command sanitization.
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Development & Release
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
git clone https://github.com/IvanGlazyrin/adonis-ai-kit.git
|
|
297
|
+
cd adonis-ai-kit
|
|
298
|
+
npm install
|
|
299
|
+
npm run build
|
|
300
|
+
npm run test:regression
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
Consume the local build from an AdonisJS app without publishing:
|
|
304
|
+
|
|
305
|
+
```bash
|
|
306
|
+
# in the app
|
|
307
|
+
npm install ../path/to/adonis-ai-kit # file: dependency
|
|
308
|
+
# or globally
|
|
309
|
+
cd ../adonis-ai-kit && npm link
|
|
310
|
+
cd ../my-app && npm link adonis-ai-kit
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Releases are published to npm by pushing a version tag:
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
npm version patch # bumps package.json + tag v1.1.1
|
|
317
|
+
git push --follow-tags # .github/workflows/release.yml runs npm publish
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
The workflow requires an `NPM_TOKEN` secret in the repository settings.
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## License
|
|
325
|
+
|
|
326
|
+
MIT © [Ivan Glazyrin](https://github.com/IvanGlazyrin) (glazrus97@gmail.com)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import type { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
export default class AiTestCommand extends BaseCommand {
|
|
4
|
+
static commandName: string;
|
|
5
|
+
static description: string;
|
|
6
|
+
static options: CommandOptions;
|
|
7
|
+
provider?: string;
|
|
8
|
+
model?: string;
|
|
9
|
+
testPrompt: string;
|
|
10
|
+
run(): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=ai_test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai_test.d.ts","sourceRoot":"","sources":["../../commands/ai_test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAS,MAAM,oBAAoB,CAAA;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAE9D,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,WAAW;IACpD,MAAM,CAAC,WAAW,SAAY;IAC9B,MAAM,CAAC,WAAW,SAA4E;IAE9F,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,QAAQ,CAAC,EAAE,MAAM,CAAA;IAGjB,KAAK,CAAC,EAAE,MAAM,CAAA;IAOd,UAAU,EAAE,MAAM,CAAA;IAEpB,GAAG;CA4EV"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { BaseCommand, flags } from '@adonisjs/core/ace';
|
|
11
|
+
export default class AiTestCommand extends BaseCommand {
|
|
12
|
+
static commandName = 'ai:test';
|
|
13
|
+
static description = 'Test connectivity, latency, and generation with configured AI providers';
|
|
14
|
+
static options = {
|
|
15
|
+
startApp: true,
|
|
16
|
+
};
|
|
17
|
+
async run() {
|
|
18
|
+
const ai = await this.app.container.make('ai');
|
|
19
|
+
this.ui.logger.info('🤖 Testing Adonis AI Provider Connectivity...\n');
|
|
20
|
+
const config = ai.getConfig();
|
|
21
|
+
const providersToTest = this.provider ? [this.provider] : Object.keys(config.providers);
|
|
22
|
+
if (providersToTest.length === 0) {
|
|
23
|
+
this.ui.logger.error('No AI providers configured in config/ai.ts!');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const table = this.ui.table();
|
|
27
|
+
table.head(['Provider', 'Model', 'Status', 'Latency', 'Circuit Breaker', 'Cost ($)']);
|
|
28
|
+
for (const pName of providersToTest) {
|
|
29
|
+
const pConfig = config.providers[pName];
|
|
30
|
+
if (!pConfig) {
|
|
31
|
+
this.ui.logger.error(`Provider "${pName}" not found in config!`);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const testModel = this.model ?? pConfig.defaultModel ?? 'default';
|
|
35
|
+
const start = Date.now();
|
|
36
|
+
try {
|
|
37
|
+
const response = await ai.generateText({
|
|
38
|
+
provider: pName,
|
|
39
|
+
model: this.model,
|
|
40
|
+
prompt: this.testPrompt || "Respond with PONG and your model identity in 10 words or less.",
|
|
41
|
+
maxOutputTokens: 50,
|
|
42
|
+
agentName: 'ace-ai-test',
|
|
43
|
+
});
|
|
44
|
+
const latency = Date.now() - start;
|
|
45
|
+
const stats = ai.getCircuitBreaker().getStats(`${response.provider}:${response.model}`);
|
|
46
|
+
// A fallback provider answering is NOT a pass for the provider under test
|
|
47
|
+
const answeredByFallback = response.provider !== pName;
|
|
48
|
+
table.row([
|
|
49
|
+
this.colors.cyan(pName),
|
|
50
|
+
response.model,
|
|
51
|
+
answeredByFallback
|
|
52
|
+
? this.colors.yellow(`⚠ FALLBACK (${response.provider})`)
|
|
53
|
+
: this.colors.green('✔ OK'),
|
|
54
|
+
`${latency}ms`,
|
|
55
|
+
this.colors.green(stats.state.toUpperCase()),
|
|
56
|
+
response.costUsd !== undefined ? `$${response.costUsd.toFixed(6)}` : 'N/A',
|
|
57
|
+
]);
|
|
58
|
+
this.ui.logger.info(`[${pName}] Output: "${response.text.trim()}"`);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const latency = Date.now() - start;
|
|
62
|
+
const stats = ai.getCircuitBreaker().getStats(`${pName}:${testModel}`);
|
|
63
|
+
table.row([
|
|
64
|
+
this.colors.cyan(pName),
|
|
65
|
+
testModel,
|
|
66
|
+
this.colors.red('✖ FAILED'),
|
|
67
|
+
`${latency}ms`,
|
|
68
|
+
this.colors.red(stats.state.toUpperCase()),
|
|
69
|
+
'N/A',
|
|
70
|
+
]);
|
|
71
|
+
this.ui.logger.error(`[${pName}] Error: ${err.message}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
this.ui.logger.log('');
|
|
75
|
+
table.render();
|
|
76
|
+
this.ui.logger.log('');
|
|
77
|
+
const totalCost = ai.getCostTracker().getTotalCost();
|
|
78
|
+
const totalTokens = ai.getCostTracker().getTotalTokens();
|
|
79
|
+
this.ui.logger.info(`Total session cost: ${totalCost.toFixed(6)} | Tokens: ${totalTokens}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
__decorate([
|
|
83
|
+
flags.string({ description: 'Specific provider to test (e.g. openai, anthropic, gemini, groq, ollama)' }),
|
|
84
|
+
__metadata("design:type", String)
|
|
85
|
+
], AiTestCommand.prototype, "provider", void 0);
|
|
86
|
+
__decorate([
|
|
87
|
+
flags.string({ description: 'Model to test with' }),
|
|
88
|
+
__metadata("design:type", String)
|
|
89
|
+
], AiTestCommand.prototype, "model", void 0);
|
|
90
|
+
__decorate([
|
|
91
|
+
flags.string({
|
|
92
|
+
name: 'prompt',
|
|
93
|
+
description: 'Test prompt to send',
|
|
94
|
+
default: 'Respond with "PONG" and your model identity in 10 words or less.',
|
|
95
|
+
}),
|
|
96
|
+
__metadata("design:type", String)
|
|
97
|
+
], AiTestCommand.prototype, "testPrompt", void 0);
|
|
98
|
+
//# sourceMappingURL=ai_test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai_test.js","sourceRoot":"","sources":["../../commands/ai_test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CommandMetaData } from '@adonisjs/core/types/ace';
|
|
2
|
+
import type { BaseCommand } from '@adonisjs/core/ace';
|
|
3
|
+
type CommandClass = typeof BaseCommand;
|
|
4
|
+
/**
|
|
5
|
+
* Ace command loader contract: returns metadata of every command in this package.
|
|
6
|
+
* Metadata is derived from the command classes themselves, so no generated `commands.json` is needed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getMetaData(): Promise<CommandMetaData[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Ace command loader contract: resolves the command class for the given metadata.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getCommand(metaData: Pick<CommandMetaData, 'commandName'>): Promise<CommandClass | null>;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD,KAAK,YAAY,GAAG,OAAO,WAAW,CAAA;AActC;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAM9D;AAED;;GAEG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAG7G"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const loaders = [
|
|
2
|
+
() => import('./ai_test.js'),
|
|
3
|
+
() => import('./make_agent.js'),
|
|
4
|
+
];
|
|
5
|
+
let cachedMetaData;
|
|
6
|
+
const cachedCommands = new Map();
|
|
7
|
+
async function loadAll() {
|
|
8
|
+
return Promise.all(loaders.map(async (load) => (await load()).default));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Ace command loader contract: returns metadata of every command in this package.
|
|
12
|
+
* Metadata is derived from the command classes themselves, so no generated `commands.json` is needed.
|
|
13
|
+
*/
|
|
14
|
+
export async function getMetaData() {
|
|
15
|
+
if (cachedMetaData)
|
|
16
|
+
return cachedMetaData;
|
|
17
|
+
const commands = await loadAll();
|
|
18
|
+
for (const command of commands)
|
|
19
|
+
cachedCommands.set(command.commandName, command);
|
|
20
|
+
cachedMetaData = commands.map((command) => command.serialize());
|
|
21
|
+
return cachedMetaData;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Ace command loader contract: resolves the command class for the given metadata.
|
|
25
|
+
*/
|
|
26
|
+
export async function getCommand(metaData) {
|
|
27
|
+
if (cachedCommands.size === 0)
|
|
28
|
+
await getMetaData();
|
|
29
|
+
return cachedCommands.get(metaData.commandName) ?? null;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../commands/main.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,GAAoD;IAC/D,GAAG,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC;IAC5B,GAAG,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC;CAChC,CAAA;AAED,IAAI,cAA6C,CAAA;AACjD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAwB,CAAA;AAEtD,KAAK,UAAU,OAAO;IACpB,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAA;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,IAAI,cAAc;QAAE,OAAO,cAAc,CAAA;IACzC,MAAM,QAAQ,GAAG,MAAM,OAAO,EAAE,CAAA;IAChC,KAAK,MAAM,OAAO,IAAI,QAAQ;QAAE,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAChF,cAAc,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,CAAA;IAC/D,OAAO,cAAc,CAAA;AACvB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,QAA8C;IAC7E,IAAI,cAAc,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,WAAW,EAAE,CAAA;IAClD,OAAO,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAA;AACzD,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseCommand } from '@adonisjs/core/ace';
|
|
2
|
+
import type { CommandOptions } from '@adonisjs/core/types/ace';
|
|
3
|
+
export default class MakeAiAgentCommand extends BaseCommand {
|
|
4
|
+
static commandName: string;
|
|
5
|
+
static description: string;
|
|
6
|
+
static options: CommandOptions;
|
|
7
|
+
name: string;
|
|
8
|
+
provider: string;
|
|
9
|
+
model: string;
|
|
10
|
+
maxTokens: number;
|
|
11
|
+
run(): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=make_agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make_agent.d.ts","sourceRoot":"","sources":["../../commands/make_agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAe,MAAM,oBAAoB,CAAA;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAI9D,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,WAAW;IACzD,MAAM,CAAC,WAAW,SAAkB;IACpC,MAAM,CAAC,WAAW,SAAqD;IAEvE,MAAM,CAAC,OAAO,EAAE,cAAc,CAE7B;IAGO,IAAI,EAAE,MAAM,CAAA;IAGZ,QAAQ,EAAE,MAAM,CAAA;IAGhB,KAAK,EAAE,MAAM,CAAA;IAGb,SAAS,EAAE,MAAM,CAAA;IAEnB,GAAG;CAqFV"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { BaseCommand, args, flags } from '@adonisjs/core/ace';
|
|
11
|
+
import fs from 'node:fs/promises';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
export default class MakeAiAgentCommand extends BaseCommand {
|
|
14
|
+
static commandName = 'make:ai-agent';
|
|
15
|
+
static description = 'Scaffold a production-grade typed AI Agent class';
|
|
16
|
+
static options = {
|
|
17
|
+
startApp: false,
|
|
18
|
+
};
|
|
19
|
+
async run() {
|
|
20
|
+
const rawName = this.name.replace(/Agent$/i, '');
|
|
21
|
+
const className = rawName.charAt(0).toUpperCase() + rawName.slice(1) + 'Agent';
|
|
22
|
+
const fileName = rawName.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase() + '_agent.ts';
|
|
23
|
+
const targetDir = path.join(this.app.makePath('app/ai/agents'));
|
|
24
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
25
|
+
const targetFile = path.join(targetDir, fileName);
|
|
26
|
+
const exists = await fs
|
|
27
|
+
.access(targetFile)
|
|
28
|
+
.then(() => true)
|
|
29
|
+
.catch(() => false);
|
|
30
|
+
if (exists) {
|
|
31
|
+
this.ui.logger.error(`Agent file already exists: ${targetFile}`);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const template = `import ai from 'adonis-ai-kit/services/main'
|
|
35
|
+
import type { GenerateTextResult, StreamTextResult } from 'adonis-ai-kit/types'
|
|
36
|
+
|
|
37
|
+
export interface ${className}Input {
|
|
38
|
+
prompt: string
|
|
39
|
+
context?: Record<string, unknown>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* ${className}
|
|
44
|
+
* Production-ready AI agent with token budgeting and structured execution.
|
|
45
|
+
*/
|
|
46
|
+
export class ${className} {
|
|
47
|
+
public readonly agentName = '${rawName.toLowerCase()}-agent'
|
|
48
|
+
public readonly defaultProvider = ${this.provider ? `'${this.provider}'` : 'undefined'}
|
|
49
|
+
public readonly defaultModel = ${this.model ? `'${this.model}'` : 'undefined'}
|
|
50
|
+
public readonly maxOutputTokens = ${this.maxTokens}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* System prompt establishing role, instructions, and guardrails.
|
|
54
|
+
*/
|
|
55
|
+
protected getSystemPrompt(): string {
|
|
56
|
+
return [
|
|
57
|
+
'You are ${className}, an expert AI assistant.',
|
|
58
|
+
'Provide concise, accurate, and actionable answers.',
|
|
59
|
+
'Always follow system security guidelines.',
|
|
60
|
+
].join('\\n')
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Executes synchronous text generation.
|
|
65
|
+
*/
|
|
66
|
+
async run(input: ${className}Input): Promise<GenerateTextResult> {
|
|
67
|
+
return ai.generateText({
|
|
68
|
+
agentName: this.agentName,
|
|
69
|
+
provider: this.defaultProvider,
|
|
70
|
+
model: this.defaultModel,
|
|
71
|
+
maxOutputTokens: this.maxOutputTokens,
|
|
72
|
+
system: this.getSystemPrompt(),
|
|
73
|
+
prompt: input.prompt,
|
|
74
|
+
metadata: input.context,
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Streams response in real time (e.g. for Server-Sent Events or WebSockets).
|
|
80
|
+
*/
|
|
81
|
+
async stream(input: ${className}Input): Promise<StreamTextResult> {
|
|
82
|
+
return ai.streamText({
|
|
83
|
+
agentName: this.agentName,
|
|
84
|
+
provider: this.defaultProvider,
|
|
85
|
+
model: this.defaultModel,
|
|
86
|
+
maxOutputTokens: this.maxOutputTokens,
|
|
87
|
+
system: this.getSystemPrompt(),
|
|
88
|
+
prompt: input.prompt,
|
|
89
|
+
metadata: input.context,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export default new ${className}()
|
|
95
|
+
`;
|
|
96
|
+
await fs.writeFile(targetFile, template.trim() + '\n', 'utf8');
|
|
97
|
+
this.ui.logger.success(`Created AI Agent: ${targetFile}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
__decorate([
|
|
101
|
+
args.string({ description: 'Name of the AI Agent class (e.g. MarketAnalysis, SupportChat, Coder)' }),
|
|
102
|
+
__metadata("design:type", String)
|
|
103
|
+
], MakeAiAgentCommand.prototype, "name", void 0);
|
|
104
|
+
__decorate([
|
|
105
|
+
flags.string({ description: 'AI provider to use (default: app default)', default: '' }),
|
|
106
|
+
__metadata("design:type", String)
|
|
107
|
+
], MakeAiAgentCommand.prototype, "provider", void 0);
|
|
108
|
+
__decorate([
|
|
109
|
+
flags.string({ description: 'Model to use (e.g. gpt-4o, claude-3-7-sonnet-latest)', default: '' }),
|
|
110
|
+
__metadata("design:type", String)
|
|
111
|
+
], MakeAiAgentCommand.prototype, "model", void 0);
|
|
112
|
+
__decorate([
|
|
113
|
+
flags.number({ description: 'Max token budget per request', default: 2048 }),
|
|
114
|
+
__metadata("design:type", Number)
|
|
115
|
+
], MakeAiAgentCommand.prototype, "maxTokens", void 0);
|
|
116
|
+
//# sourceMappingURL=make_agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"make_agent.js","sourceRoot":"","sources":["../../commands/make_agent.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE7D,OAAO,EAAE,MAAM,kBAAkB,CAAA;AACjC,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,WAAW;IACzD,MAAM,CAAC,WAAW,GAAG,eAAe,CAAA;IACpC,MAAM,CAAC,WAAW,GAAG,kDAAkD,CAAA;IAEvE,MAAM,CAAC,OAAO,GAAmB;QAC/B,QAAQ,EAAE,KAAK;KAChB,CAAA;IAcD,KAAK,CAAC,GAAG;QACP,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAA;QAChD,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,OAAO,CAAA;QAC9E,MAAM,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,oBAAoB,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,GAAG,WAAW,CAAA;QAE3F,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAA;QAC/D,MAAM,EAAE,CAAC,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAA;QAEjD,MAAM,MAAM,GAAG,MAAM,EAAE;aACpB,MAAM,CAAC,UAAU,CAAC;aAClB,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;aAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAA;QAErB,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,UAAU,EAAE,CAAC,CAAA;YAChE,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG;;;mBAGF,SAAS;;;;;;KAMvB,SAAS;;;eAGC,SAAS;iCACS,OAAO,CAAC,WAAW,EAAE;sCAChB,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,WAAW;mCACrD,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,WAAW;sCACzC,IAAI,CAAC,SAAS;;;;;;;iBAOnC,SAAS;;;;;;;;;qBASL,SAAS;;;;;;;;;;;;;;;wBAeN,SAAS;;;;;;;;;;;;;qBAaZ,SAAS;CAC7B,CAAA;QAEG,MAAM,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,CAAA;QAC9D,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,qBAAqB,UAAU,EAAE,CAAC,CAAA;IAC3D,CAAC;;AA/FO;IADP,IAAI,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sEAAsE,EAAE,CAAC;;gDACjF;AAGZ;IADP,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,2CAA2C,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;;oDAChE;AAGhB;IADP,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,sDAAsD,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC;;iDAC9E;AAGb;IADP,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,EAAE,8BAA8B,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;qDACpD"}
|