@genui-a3/create 0.1.4 → 0.1.6
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 +24 -5
- package/dist/index.js +42 -32
- package/package.json +1 -1
- package/template/app/api/agui/route.ts +2 -2
- package/template/app/api/chat/route.ts +2 -2
- package/template/app/api/stream/route.ts +2 -2
- package/template/app/components/molecules/ChatInput.tsx +14 -1
- package/template/app/lib/providers/anthropic.ts +5 -0
- package/template/app/lib/providers/bedrock.ts +5 -0
- package/template/app/lib/providers/openai.ts +3 -0
- package/template/package.json +2 -2
package/README.md
CHANGED
|
@@ -5,14 +5,16 @@ Scaffold a new [A3](https://www.npmjs.com/package/@genui-a3/core) agentic app in
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx @genui-a3/create@latest
|
|
8
|
+
npx @genui-a3/create@latest
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
This will:
|
|
11
|
+
This starts an **interactive session** that will:
|
|
12
12
|
|
|
13
|
-
1.
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
1. Prompt for your project name and directory
|
|
14
|
+
2. Let you select and configure LLM providers (OpenAI, Anthropic, Bedrock)
|
|
15
|
+
3. Generate a production-ready Next.js template
|
|
16
|
+
4. Automatically create your `.env` with the provided credentials
|
|
17
|
+
5. Install all dependencies
|
|
16
18
|
|
|
17
19
|
Then start developing:
|
|
18
20
|
|
|
@@ -34,6 +36,23 @@ A fully configured Next.js application with:
|
|
|
34
36
|
- **Material UI** — pre-configured theming with MUI components
|
|
35
37
|
- **TypeScript** — strict type-checking out of the box
|
|
36
38
|
|
|
39
|
+
## Configuration & Providers
|
|
40
|
+
|
|
41
|
+
The CLI guides you through setting up your LLM providers and authentication during scaffolding.
|
|
42
|
+
|
|
43
|
+
### Supported Providers
|
|
44
|
+
|
|
45
|
+
| Provider | Authentication | Auto-Generated Config |
|
|
46
|
+
|----------|---------------|------------------------|
|
|
47
|
+
| **OpenAI** | API Key | `OPENAI_API_KEY` |
|
|
48
|
+
| **Anthropic** | API Key | `ANTHROPIC_API_KEY` |
|
|
49
|
+
| **AWS Bedrock** | AWS Profile or Access Keys | `AWS_REGION`, `AWS_ACCESS_KEY_ID`, etc. |
|
|
50
|
+
|
|
51
|
+
### Automatic Setup
|
|
52
|
+
|
|
53
|
+
- **Environment Variables**: A `.env` file is generated with your keys so you can run the app immediately.
|
|
54
|
+
- **Provider Registry**: The CLI generates `app/lib/provider.ts`, pre-configuring the A3 `Provider` factory with your primary model selection.
|
|
55
|
+
|
|
37
56
|
## Usage
|
|
38
57
|
|
|
39
58
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -14,45 +14,39 @@ import fs from "fs-extra";
|
|
|
14
14
|
|
|
15
15
|
// src/utils/providers.ts
|
|
16
16
|
var PROVIDER_META = {
|
|
17
|
-
openai: {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
factory: "createOpenAIProvider",
|
|
21
|
-
models: ["gpt-4o", "gpt-4o-mini"]
|
|
22
|
-
},
|
|
23
|
-
bedrock: {
|
|
24
|
-
label: "AWS Bedrock",
|
|
25
|
-
importPath: "@genui-a3/providers/bedrock",
|
|
26
|
-
factory: "createBedrockProvider",
|
|
27
|
-
models: ["us.anthropic.claude-sonnet-4-5-20250929-v1:0", "us.anthropic.claude-haiku-4-5-20251001-v1:0"]
|
|
28
|
-
}
|
|
17
|
+
openai: { label: "OpenAI", exportName: "openaiProvider", file: "openai.ts" },
|
|
18
|
+
bedrock: { label: "AWS Bedrock", exportName: "bedrockProvider", file: "bedrock.ts" },
|
|
19
|
+
anthropic: { label: "Anthropic", exportName: "anthropicProvider", file: "anthropic.ts" }
|
|
29
20
|
};
|
|
30
21
|
|
|
31
22
|
// src/utils/generators.ts
|
|
32
|
-
function
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
export function getProvider(): Provider {
|
|
41
|
-
if (!_provider) {
|
|
42
|
-
_provider = ${factory}({
|
|
43
|
-
models: [${modelsLiteral}],
|
|
44
|
-
})
|
|
23
|
+
function generateProviderFiles(targetDir, config) {
|
|
24
|
+
const providersDir = path.join(targetDir, "app", "lib", "providers");
|
|
25
|
+
for (const [key, meta] of Object.entries(PROVIDER_META)) {
|
|
26
|
+
if (!config.providers.includes(key)) {
|
|
27
|
+
const filePath = path.join(providersDir, meta.file);
|
|
28
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
29
|
+
}
|
|
45
30
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
31
|
+
const primaryMeta = PROVIDER_META[config.primaryProvider];
|
|
32
|
+
const lines = [];
|
|
33
|
+
for (const provKey of config.providers) {
|
|
34
|
+
const meta = PROVIDER_META[provKey];
|
|
35
|
+
const baseName = meta.file.replace(".ts", "");
|
|
36
|
+
lines.push(`export { ${meta.exportName} } from './${baseName}'`);
|
|
37
|
+
}
|
|
38
|
+
lines.push(`export { ${primaryMeta.exportName} as provider } from './${primaryMeta.file.replace(".ts", "")}'`);
|
|
39
|
+
lines.push("");
|
|
40
|
+
fs.outputFileSync(path.join(providersDir, "index.ts"), lines.join("\n"));
|
|
50
41
|
}
|
|
51
42
|
function generateEnvFile(targetDir, config) {
|
|
52
43
|
const lines = ["# Generated by create-genui-a3", "# This file is git-ignored. Do not commit credentials.", ""];
|
|
53
44
|
if (config.providers.includes("openai")) {
|
|
54
45
|
lines.push("# OpenAI", `OPENAI_API_KEY=${config.openaiApiKey || ""}`, "");
|
|
55
46
|
}
|
|
47
|
+
if (config.providers.includes("anthropic")) {
|
|
48
|
+
lines.push("# Anthropic", `ANTHROPIC_API_KEY=${config.anthropicApiKey || ""}`, "");
|
|
49
|
+
}
|
|
56
50
|
if (config.providers.includes("bedrock")) {
|
|
57
51
|
if (config.bedrockAuthMode === "profile") {
|
|
58
52
|
lines.push("# AWS Bedrock (profile mode)", `AWS_PROFILE=${config.awsProfile || "default"}`);
|
|
@@ -187,6 +181,18 @@ async function promptOpenAIConfig(config) {
|
|
|
187
181
|
handleCancel(openaiApiKey);
|
|
188
182
|
config.openaiApiKey = openaiApiKey;
|
|
189
183
|
}
|
|
184
|
+
async function promptAnthropicConfig(config) {
|
|
185
|
+
p.log.step(PROVIDER_META.anthropic.label);
|
|
186
|
+
const anthropicApiKey = await p.text({
|
|
187
|
+
message: "Anthropic API key:",
|
|
188
|
+
placeholder: "sk-ant-...",
|
|
189
|
+
validate(input) {
|
|
190
|
+
if (!input) return "API key is required. Get one at https://console.anthropic.com/settings/keys";
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
handleCancel(anthropicApiKey);
|
|
194
|
+
config.anthropicApiKey = anthropicApiKey;
|
|
195
|
+
}
|
|
190
196
|
async function promptBedrockConfig(config) {
|
|
191
197
|
p.log.step(PROVIDER_META.bedrock.label);
|
|
192
198
|
const authMode = await p.select({
|
|
@@ -281,7 +287,8 @@ async function promptProviders() {
|
|
|
281
287
|
message: "Which LLM provider(s) do you want to configure?",
|
|
282
288
|
options: [
|
|
283
289
|
{ label: "OpenAI", value: "openai" },
|
|
284
|
-
{ label: "AWS Bedrock", value: "bedrock" }
|
|
290
|
+
{ label: "AWS Bedrock", value: "bedrock" },
|
|
291
|
+
{ label: "Anthropic", value: "anthropic" }
|
|
285
292
|
],
|
|
286
293
|
required: true
|
|
287
294
|
});
|
|
@@ -296,6 +303,9 @@ async function promptProviders() {
|
|
|
296
303
|
if (providers.includes("bedrock")) {
|
|
297
304
|
await promptBedrockConfig(config);
|
|
298
305
|
}
|
|
306
|
+
if (providers.includes("anthropic")) {
|
|
307
|
+
await promptAnthropicConfig(config);
|
|
308
|
+
}
|
|
299
309
|
if (providers.length > 1) {
|
|
300
310
|
await promptPrimaryProvider(providers, config);
|
|
301
311
|
}
|
|
@@ -359,8 +369,8 @@ async function main() {
|
|
|
359
369
|
const spin = p2.spinner();
|
|
360
370
|
spin.start("Scaffolding project files");
|
|
361
371
|
scaffoldProject(templateDir, targetDir, projectName);
|
|
362
|
-
spin.message("Configuring
|
|
363
|
-
|
|
372
|
+
spin.message("Configuring providers");
|
|
373
|
+
generateProviderFiles(targetDir, providerConfig);
|
|
364
374
|
spin.message("Generating .env file");
|
|
365
375
|
generateEnvFile(targetDir, providerConfig);
|
|
366
376
|
spin.stop("Project scaffolded");
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { NextRequest } from 'next/server'
|
|
|
2
2
|
import { EventType, type RunAgentInput } from '@ag-ui/client'
|
|
3
3
|
import { EventEncoder } from '@ag-ui/encoder'
|
|
4
4
|
import { AgentRegistry, ChatSession, MemorySessionStore, AGUIAgent } from '@genui-a3/core'
|
|
5
|
-
import {
|
|
5
|
+
import { provider } from '../../lib/providers'
|
|
6
6
|
import { greetingAgent, State } from '../../agents/greeting'
|
|
7
7
|
import { ageAgent } from '../../agents/age'
|
|
8
8
|
|
|
@@ -23,7 +23,7 @@ const a3Agent = new AGUIAgent({
|
|
|
23
23
|
sessionId: input.threadId,
|
|
24
24
|
store,
|
|
25
25
|
initialAgentId: 'greeting',
|
|
26
|
-
provider
|
|
26
|
+
provider,
|
|
27
27
|
}),
|
|
28
28
|
})
|
|
29
29
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { NextRequest, NextResponse } from 'next/server'
|
|
7
7
|
import { AgentRegistry, ChatSession, MemorySessionStore } from '@genui-a3/core'
|
|
8
|
-
import {
|
|
8
|
+
import { provider } from '../../lib/providers'
|
|
9
9
|
import { greetingAgent, State } from '../../agents/greeting'
|
|
10
10
|
import { ageAgent } from '../../agents/age'
|
|
11
11
|
|
|
@@ -36,7 +36,7 @@ export async function POST(request: NextRequest) {
|
|
|
36
36
|
store,
|
|
37
37
|
initialAgentId: 'greeting',
|
|
38
38
|
initialState: { userName: undefined },
|
|
39
|
-
provider
|
|
39
|
+
provider,
|
|
40
40
|
})
|
|
41
41
|
|
|
42
42
|
const result = await session.send({ message })
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { NextRequest } from 'next/server'
|
|
2
2
|
import { AgentRegistry, ChatSession, MemorySessionStore } from '@genui-a3/core'
|
|
3
|
-
import {
|
|
3
|
+
import { provider } from '../../lib/providers'
|
|
4
4
|
import { greetingAgent, State } from '../../agents/greeting'
|
|
5
5
|
import { ageAgent } from '../../agents/age'
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ export async function POST(request: NextRequest) {
|
|
|
31
31
|
store,
|
|
32
32
|
initialAgentId: 'greeting',
|
|
33
33
|
initialState: { userName: undefined },
|
|
34
|
-
provider
|
|
34
|
+
provider,
|
|
35
35
|
})
|
|
36
36
|
|
|
37
37
|
const encoder = new TextEncoder()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useCallback } from 'react'
|
|
3
|
+
import { useState, useCallback, useEffect, useRef } from 'react'
|
|
4
4
|
import styled from 'styled-components'
|
|
5
5
|
import { TextField, Button, Box } from '@mui/material'
|
|
6
6
|
import type { Theme } from '@mui/material/styles'
|
|
@@ -26,6 +26,18 @@ const InputForm = styled.form`
|
|
|
26
26
|
|
|
27
27
|
export function ChatInput({ onSubmit, disabled, placeholder = 'Type a message...' }: Props) {
|
|
28
28
|
const [value, setValue] = useState('')
|
|
29
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
30
|
+
const prevDisabled = useRef(disabled)
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
// When transitioning from disabled (true) back to enabled (false), restore focus
|
|
34
|
+
if (prevDisabled.current && !disabled) {
|
|
35
|
+
// Small timeout to ensure the clear-disabled DOM update finishes
|
|
36
|
+
const timer = setTimeout(() => inputRef.current?.focus(), 10)
|
|
37
|
+
return () => clearTimeout(timer)
|
|
38
|
+
}
|
|
39
|
+
prevDisabled.current = disabled
|
|
40
|
+
}, [disabled])
|
|
29
41
|
|
|
30
42
|
const handleSubmit = useCallback(
|
|
31
43
|
(e: React.FormEvent) => {
|
|
@@ -42,6 +54,7 @@ export function ChatInput({ onSubmit, disabled, placeholder = 'Type a message...
|
|
|
42
54
|
<InputContainer>
|
|
43
55
|
<InputForm onSubmit={handleSubmit}>
|
|
44
56
|
<TextField
|
|
57
|
+
inputRef={inputRef}
|
|
45
58
|
fullWidth
|
|
46
59
|
value={value}
|
|
47
60
|
onChange={(e) => setValue(e.target.value)}
|
package/template/package.json
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"@ag-ui/encoder": "0.0.45",
|
|
12
12
|
"@emotion/react": "11.14.0",
|
|
13
13
|
"@emotion/styled": "11.14.1",
|
|
14
|
-
"@genui-a3/core": "^0.1.
|
|
15
|
-
"@genui-a3/providers": "^0.0.
|
|
14
|
+
"@genui-a3/core": "^0.1.10",
|
|
15
|
+
"@genui-a3/providers": "^0.0.2",
|
|
16
16
|
"@mui/icons-material": "7.3.7",
|
|
17
17
|
"@mui/material": "7.3.7",
|
|
18
18
|
"next": "16.1.6",
|