@genui/a3-create 0.1.36
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 +123 -0
- package/dist/index.js +684 -0
- package/package.json +52 -0
- package/template/.cursor/rules/example-app.mdc +9 -0
- package/template/CLAUDE.md +121 -0
- package/template/README.md +20 -0
- package/template/_gitignore +36 -0
- package/template/app/ThemeProvider.tsx +17 -0
- package/template/app/agents/age.ts +25 -0
- package/template/app/agents/greeting.ts +30 -0
- package/template/app/agents/index.ts +57 -0
- package/template/app/agents/onboarding/index.ts +15 -0
- package/template/app/agents/onboarding/prompt.ts +59 -0
- package/template/app/agents/registry.ts +17 -0
- package/template/app/agents/state.ts +10 -0
- package/template/app/api/agui/route.ts +56 -0
- package/template/app/api/chat/route.ts +35 -0
- package/template/app/api/stream/route.ts +57 -0
- package/template/app/apple-icon-dark.png +0 -0
- package/template/app/apple-icon.png +0 -0
- package/template/app/components/atoms/AgentNode.tsx +56 -0
- package/template/app/components/atoms/AppLogo.tsx +44 -0
- package/template/app/components/atoms/ChatContainer.tsx +13 -0
- package/template/app/components/atoms/ChatHeader.tsx +49 -0
- package/template/app/components/atoms/MarkdownRenderer.tsx +134 -0
- package/template/app/components/atoms/MessageBubble.tsx +21 -0
- package/template/app/components/atoms/TransitionEdge.tsx +49 -0
- package/template/app/components/atoms/index.ts +7 -0
- package/template/app/components/molecules/ChatInput.tsx +94 -0
- package/template/app/components/molecules/ChatMessage.tsx +45 -0
- package/template/app/components/molecules/index.ts +2 -0
- package/template/app/components/organisms/AgentGraph.tsx +75 -0
- package/template/app/components/organisms/AguiChat.tsx +133 -0
- package/template/app/components/organisms/Chat.tsx +88 -0
- package/template/app/components/organisms/ChatMessageList.tsx +35 -0
- package/template/app/components/organisms/ExamplePageLayout.tsx +118 -0
- package/template/app/components/organisms/OnboardingChat.tsx +24 -0
- package/template/app/components/organisms/Sidebar.tsx +147 -0
- package/template/app/components/organisms/SidebarLayout.tsx +58 -0
- package/template/app/components/organisms/StateViewer.tsx +126 -0
- package/template/app/components/organisms/StreamChat.tsx +173 -0
- package/template/app/components/organisms/index.ts +10 -0
- package/template/app/constants/chat.ts +52 -0
- package/template/app/constants/paths.ts +1 -0
- package/template/app/constants/ui.ts +61 -0
- package/template/app/examples/agui/page.tsx +26 -0
- package/template/app/examples/chat/page.tsx +26 -0
- package/template/app/examples/page.tsx +106 -0
- package/template/app/examples/stream/page.tsx +26 -0
- package/template/app/favicon-dark.ico +0 -0
- package/template/app/favicon.ico +0 -0
- package/template/app/icon.svg +13 -0
- package/template/app/layout.tsx +36 -0
- package/template/app/lib/actions/restartSession.ts +10 -0
- package/template/app/lib/getAgentGraphData.ts +43 -0
- package/template/app/lib/getGraphLayout.ts +99 -0
- package/template/app/lib/hooks/useRestart.ts +33 -0
- package/template/app/lib/parseTransitionTargets.ts +140 -0
- package/template/app/lib/providers/anthropic.ts +12 -0
- package/template/app/lib/providers/bedrock.ts +12 -0
- package/template/app/lib/providers/openai.ts +10 -0
- package/template/app/onboarding/page.tsx +21 -0
- package/template/app/page.tsx +16 -0
- package/template/app/styled.d.ts +6 -0
- package/template/app/theme.ts +22 -0
- package/template/docs/A3-README.md +121 -0
- package/template/docs/API-REFERENCE.md +85 -0
- package/template/docs/ARCHITECTURE.md +84 -0
- package/template/docs/CORE-CONCEPTS.md +347 -0
- package/template/docs/CUSTOM_LOGGING.md +36 -0
- package/template/docs/CUSTOM_PROVIDERS.md +642 -0
- package/template/docs/CUSTOM_STORES.md +228 -0
- package/template/docs/PROVIDER-ANTHROPIC.md +45 -0
- package/template/docs/PROVIDER-BEDROCK.md +45 -0
- package/template/docs/PROVIDER-OPENAI.md +47 -0
- package/template/docs/PROVIDERS.md +124 -0
- package/template/docs/QUICK-START-EXAMPLES.md +197 -0
- package/template/docs/RESILIENCE.md +226 -0
- package/template/docs/TRANSITIONS.md +245 -0
- package/template/docs/WIDGETS.md +331 -0
- package/template/docs/contributing/LOGGING.md +104 -0
- package/template/docs/designs/a3-gtm-strategy.md +280 -0
- package/template/docs/designs/a3-platform-vision.md +276 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.mjs +15 -0
- package/template/package.json +41 -0
- package/template/public/android-chrome-192x192.png +0 -0
- package/template/public/android-chrome-512x512.png +0 -0
- package/template/public/site.webmanifest +11 -0
- package/template/scripts/dev.mjs +29 -0
- package/template/tsconfig.json +47 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState } from 'react'
|
|
4
|
+
import { Paper, Typography, Box, List, ListItemButton, ListItemText, Collapse } from '@mui/material'
|
|
5
|
+
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
6
|
+
import ChevronRightIcon from '@mui/icons-material/ChevronRight'
|
|
7
|
+
import { STATE_HEADING, STATE_EMPTY } from '@constants/ui'
|
|
8
|
+
|
|
9
|
+
interface StateViewerProps {
|
|
10
|
+
state: Record<string, unknown>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const VALUE_COLORS = {
|
|
14
|
+
string: '#16a34a',
|
|
15
|
+
number: '#2563eb',
|
|
16
|
+
boolean: '#ea580c',
|
|
17
|
+
null: '#9ca3af',
|
|
18
|
+
} as const
|
|
19
|
+
|
|
20
|
+
function ValueDisplay({ value }: { value: unknown }) {
|
|
21
|
+
const isNullish = value === null || value === undefined
|
|
22
|
+
const typeKey = isNullish ? 'null' : typeof value
|
|
23
|
+
const color = typeKey in VALUE_COLORS ? VALUE_COLORS[typeKey as keyof typeof VALUE_COLORS] : '#64748b'
|
|
24
|
+
|
|
25
|
+
const getContent = () => {
|
|
26
|
+
if (value === null) return 'null'
|
|
27
|
+
if (value === undefined) return 'undefined'
|
|
28
|
+
if (typeof value === 'string') return `"${value}"`
|
|
29
|
+
if (typeof value === 'object') return JSON.stringify(value)
|
|
30
|
+
if (typeof value === 'function') return `[Function${value.name ? `: ${value.name}` : ''}]`
|
|
31
|
+
if (typeof value === 'symbol') return value.toString()
|
|
32
|
+
return String(value as number | boolean | bigint)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return (
|
|
36
|
+
<Typography
|
|
37
|
+
component="span"
|
|
38
|
+
sx={{ color, fontFamily: 'monospace', fontSize: 13, fontStyle: isNullish ? 'italic' : 'normal' }}
|
|
39
|
+
>
|
|
40
|
+
{getContent()}
|
|
41
|
+
</Typography>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function StateNode({ label, value, defaultOpen = false, depth = 0 }: { label: string; value: unknown; defaultOpen?: boolean; depth?: number }) {
|
|
46
|
+
const [open, setOpen] = useState(defaultOpen)
|
|
47
|
+
|
|
48
|
+
const isExpandable = value !== null && typeof value === 'object'
|
|
49
|
+
|
|
50
|
+
if (!isExpandable) {
|
|
51
|
+
return (
|
|
52
|
+
<ListItemButton sx={{ pl: 2 + depth * 2, py: 0.25 }} disableRipple>
|
|
53
|
+
<ListItemText
|
|
54
|
+
primary={
|
|
55
|
+
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>
|
|
56
|
+
<Typography component="span" sx={{ fontFamily: 'monospace', fontSize: 13, fontWeight: 600, color: '#475569' }}>
|
|
57
|
+
{label}:
|
|
58
|
+
</Typography>
|
|
59
|
+
<ValueDisplay value={value} />
|
|
60
|
+
</Box>
|
|
61
|
+
}
|
|
62
|
+
/>
|
|
63
|
+
</ListItemButton>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const entries = Array.isArray(value)
|
|
68
|
+
? value.map((v, i) => [String(i), v] as [string, unknown])
|
|
69
|
+
: Object.entries(value as Record<string, unknown>)
|
|
70
|
+
|
|
71
|
+
return (
|
|
72
|
+
<>
|
|
73
|
+
<ListItemButton onClick={() => setOpen(!open)} sx={{ pl: 2 + depth * 2, py: 0.25 }}>
|
|
74
|
+
{open ? <ExpandMoreIcon sx={{ fontSize: 18, color: '#94a3b8' }} /> : <ChevronRightIcon sx={{ fontSize: 18, color: '#94a3b8' }} />}
|
|
75
|
+
<ListItemText
|
|
76
|
+
primary={
|
|
77
|
+
<Box sx={{ display: 'flex', gap: 1, alignItems: 'center', ml: 0.5 }}>
|
|
78
|
+
<Typography component="span" sx={{ fontFamily: 'monospace', fontSize: 13, fontWeight: 600, color: '#475569' }}>
|
|
79
|
+
{label}
|
|
80
|
+
</Typography>
|
|
81
|
+
<Typography component="span" sx={{ fontFamily: 'monospace', fontSize: 12, color: '#94a3b8' }}>
|
|
82
|
+
{Array.isArray(value) ? `[${value.length}]` : `{${entries.length}}`}
|
|
83
|
+
</Typography>
|
|
84
|
+
</Box>
|
|
85
|
+
}
|
|
86
|
+
/>
|
|
87
|
+
</ListItemButton>
|
|
88
|
+
<Collapse in={open} timeout="auto" unmountOnExit>
|
|
89
|
+
<List disablePadding>
|
|
90
|
+
{entries.map(([key, val]) => (
|
|
91
|
+
<StateNode key={key} label={key} value={val} depth={depth + 1} />
|
|
92
|
+
))}
|
|
93
|
+
</List>
|
|
94
|
+
</Collapse>
|
|
95
|
+
</>
|
|
96
|
+
)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export function StateViewer({ state }: StateViewerProps) {
|
|
100
|
+
const entries = Object.entries(state)
|
|
101
|
+
const hasData = entries.length > 0
|
|
102
|
+
|
|
103
|
+
return (
|
|
104
|
+
<Paper
|
|
105
|
+
elevation={0}
|
|
106
|
+
sx={{ p: 2, border: 1, borderColor: 'divider', borderRadius: 3, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}
|
|
107
|
+
>
|
|
108
|
+
<Typography variant="subtitle2" fontWeight="bold" gutterBottom>
|
|
109
|
+
{STATE_HEADING}
|
|
110
|
+
</Typography>
|
|
111
|
+
<Box sx={{ overflowY: 'auto', maxHeight: 300 }}>
|
|
112
|
+
{hasData ? (
|
|
113
|
+
<List disablePadding dense>
|
|
114
|
+
{entries.map(([key, value]) => (
|
|
115
|
+
<StateNode key={key} label={key} value={value} defaultOpen depth={0} />
|
|
116
|
+
))}
|
|
117
|
+
</List>
|
|
118
|
+
) : (
|
|
119
|
+
<Typography variant="body2" sx={{ color: 'text.secondary', py: 2, textAlign: 'center', fontStyle: 'italic' }}>
|
|
120
|
+
{STATE_EMPTY}
|
|
121
|
+
</Typography>
|
|
122
|
+
)}
|
|
123
|
+
</Box>
|
|
124
|
+
</Paper>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback, useRef } from 'react'
|
|
4
|
+
import { Typography } from '@mui/material'
|
|
5
|
+
import { ChatMessageList } from '@organisms/ChatMessageList'
|
|
6
|
+
import { ChatContainer, ChatHeader } from '@atoms'
|
|
7
|
+
import { ChatInput } from '@molecules'
|
|
8
|
+
import { MessageSender } from '@genui/a3'
|
|
9
|
+
import type { Message } from '@genui/a3'
|
|
10
|
+
import { EventType } from '@ag-ui/client'
|
|
11
|
+
import { CHAT_ERROR, CHAT_ERROR_SHORT, CHAT_TRANSITION } from '@constants/ui'
|
|
12
|
+
import { useRestart, type RestartResult } from '@lib/hooks/useRestart'
|
|
13
|
+
|
|
14
|
+
type StreamEvent = {
|
|
15
|
+
type: EventType
|
|
16
|
+
delta?: string
|
|
17
|
+
agentId?: string
|
|
18
|
+
result?: Record<string, unknown>
|
|
19
|
+
message?: string
|
|
20
|
+
content?: string
|
|
21
|
+
name?: string
|
|
22
|
+
value?: Record<string, unknown>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface StreamChatProps {
|
|
26
|
+
sessionId: string
|
|
27
|
+
initialMessages?: Message[]
|
|
28
|
+
onSessionUpdate?: (update: { activeAgentId: string | null; state: Record<string, unknown> }) => void
|
|
29
|
+
onRestart?: () => Promise<RestartResult>
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function StreamChat({ sessionId, initialMessages, onSessionUpdate, onRestart }: StreamChatProps) {
|
|
33
|
+
const [messages, setMessages] = useState<Message[]>(initialMessages ?? [])
|
|
34
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
35
|
+
const [isTransitioning, setIsTransitioning] = useState(false)
|
|
36
|
+
const assistantIdRef = useRef<string>('')
|
|
37
|
+
const { isRestarting, handleRestart } = useRestart({ onRestart, setMessages, onSessionUpdate })
|
|
38
|
+
|
|
39
|
+
const handleSubmit = useCallback(
|
|
40
|
+
async (text: string) => {
|
|
41
|
+
const userMsg: Message = {
|
|
42
|
+
messageId: crypto.randomUUID(),
|
|
43
|
+
text,
|
|
44
|
+
metadata: { source: MessageSender.USER },
|
|
45
|
+
}
|
|
46
|
+
setMessages((prev) => [...prev, userMsg])
|
|
47
|
+
setIsLoading(true)
|
|
48
|
+
|
|
49
|
+
// Create a placeholder assistant message for streaming into
|
|
50
|
+
let assistantId = crypto.randomUUID()
|
|
51
|
+
assistantIdRef.current = assistantId
|
|
52
|
+
|
|
53
|
+
const streamingMsg: Message = {
|
|
54
|
+
messageId: assistantId,
|
|
55
|
+
text: '',
|
|
56
|
+
metadata: { source: MessageSender.ASSISTANT },
|
|
57
|
+
isStreaming: true,
|
|
58
|
+
}
|
|
59
|
+
setMessages((prev) => [...prev, streamingMsg])
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
const response = await fetch('/api/stream', {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'Content-Type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({ message: text, sessionId }),
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw new Error(`HTTP error: ${response.status}`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const reader = response.body?.getReader()
|
|
73
|
+
if (!reader) throw new Error('No response body')
|
|
74
|
+
|
|
75
|
+
const decoder = new TextDecoder()
|
|
76
|
+
let buffer = ''
|
|
77
|
+
|
|
78
|
+
while (true) {
|
|
79
|
+
const { done, value } = await reader.read()
|
|
80
|
+
if (done) break
|
|
81
|
+
|
|
82
|
+
buffer += decoder.decode(value, { stream: true })
|
|
83
|
+
|
|
84
|
+
// Process complete SSE lines
|
|
85
|
+
const lines = buffer.split('\n')
|
|
86
|
+
buffer = lines.pop() ?? ''
|
|
87
|
+
|
|
88
|
+
for (const line of lines) {
|
|
89
|
+
if (!line.startsWith('data: ')) continue
|
|
90
|
+
const data = line.slice(6)
|
|
91
|
+
if (data === '[DONE]') continue
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const event = JSON.parse(data) as StreamEvent
|
|
95
|
+
|
|
96
|
+
if (event.type === EventType.TEXT_MESSAGE_CONTENT && event.delta) {
|
|
97
|
+
setIsTransitioning(false)
|
|
98
|
+
setMessages((prev) =>
|
|
99
|
+
prev.map((m) => (m.messageId === assistantId ? { ...m, text: m.text + event.delta } : m)),
|
|
100
|
+
)
|
|
101
|
+
} else if (event.type === EventType.CUSTOM && event.name === 'AgentTransition') {
|
|
102
|
+
const transitionEvent = event as StreamEvent & {
|
|
103
|
+
value?: { toAgentId?: string; state?: Record<string, unknown> }
|
|
104
|
+
}
|
|
105
|
+
onSessionUpdate?.({
|
|
106
|
+
activeAgentId: transitionEvent.value?.toAgentId ?? null,
|
|
107
|
+
state: transitionEvent.value?.state ?? {},
|
|
108
|
+
})
|
|
109
|
+
const prevAssistantId = assistantId
|
|
110
|
+
assistantId = crypto.randomUUID()
|
|
111
|
+
assistantIdRef.current = assistantId
|
|
112
|
+
setIsTransitioning(true)
|
|
113
|
+
setMessages((prev) => {
|
|
114
|
+
const updated = prev.map((m) => (m.messageId === prevAssistantId ? { ...m, isStreaming: false } : m))
|
|
115
|
+
return [
|
|
116
|
+
...updated,
|
|
117
|
+
{
|
|
118
|
+
messageId: assistantId,
|
|
119
|
+
text: '',
|
|
120
|
+
metadata: { source: MessageSender.ASSISTANT },
|
|
121
|
+
isStreaming: true,
|
|
122
|
+
},
|
|
123
|
+
]
|
|
124
|
+
})
|
|
125
|
+
} else if (event.type === EventType.RUN_FINISHED) {
|
|
126
|
+
setIsTransitioning(false)
|
|
127
|
+
const result = event.result
|
|
128
|
+
onSessionUpdate?.({
|
|
129
|
+
activeAgentId: (result?.activeAgentId as string) ?? null,
|
|
130
|
+
state: (result?.state as Record<string, unknown>) ?? {},
|
|
131
|
+
})
|
|
132
|
+
setMessages((prev) => prev.map((m) => (m.messageId === assistantId ? { ...m, isStreaming: false } : m)))
|
|
133
|
+
} else if (event.type === EventType.RUN_ERROR) {
|
|
134
|
+
setIsTransitioning(false)
|
|
135
|
+
setMessages((prev) =>
|
|
136
|
+
prev.map((m) =>
|
|
137
|
+
m.messageId === assistantId ? { ...m, text: m.text || CHAT_ERROR_SHORT, isStreaming: false } : m,
|
|
138
|
+
),
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
} catch {
|
|
142
|
+
// Skip malformed JSON lines
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Ensure streaming flag is cleared
|
|
148
|
+
setMessages((prev) => prev.map((m) => (m.messageId === assistantId ? { ...m, isStreaming: false } : m)))
|
|
149
|
+
} catch (error) {
|
|
150
|
+
console.error('Chat stream error:', error)
|
|
151
|
+
setMessages((prev) =>
|
|
152
|
+
prev.map((m) => (m.messageId === assistantId ? { ...m, text: m.text || CHAT_ERROR, isStreaming: false } : m)),
|
|
153
|
+
)
|
|
154
|
+
} finally {
|
|
155
|
+
setIsLoading(false)
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
[onSessionUpdate, sessionId],
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<ChatContainer elevation={0}>
|
|
163
|
+
<ChatHeader onRestart={() => void handleRestart?.()} isRestarting={isRestarting} />
|
|
164
|
+
<ChatMessageList messages={messages} />
|
|
165
|
+
{isTransitioning && (
|
|
166
|
+
<Typography variant="caption" color="textSecondary" sx={{ px: 2, pb: 1, fontStyle: 'italic' }}>
|
|
167
|
+
{CHAT_TRANSITION}
|
|
168
|
+
</Typography>
|
|
169
|
+
)}
|
|
170
|
+
<ChatInput onSubmit={handleSubmit} disabled={isLoading || isRestarting} />
|
|
171
|
+
</ChatContainer>
|
|
172
|
+
)
|
|
173
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { AguiChat } from './AguiChat'
|
|
2
|
+
export { AgentGraph } from './AgentGraph'
|
|
3
|
+
export { Chat } from './Chat'
|
|
4
|
+
export { ChatMessageList } from './ChatMessageList'
|
|
5
|
+
export { ExamplePageLayout } from './ExamplePageLayout'
|
|
6
|
+
export { OnboardingChat } from './OnboardingChat'
|
|
7
|
+
export { Sidebar } from './Sidebar'
|
|
8
|
+
export { SidebarLayout } from './SidebarLayout'
|
|
9
|
+
export { StateViewer } from './StateViewer'
|
|
10
|
+
export { StreamChat } from './StreamChat'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export const WELCOME_MESSAGE_TEXT = `Hi! I'm an A3 agent; one of potentially many that can be orchestrated together to build reliable, predictable applications.
|
|
2
|
+
|
|
3
|
+
**Using an AI Coding Assistant?** Just paste this into your Ai prompt:
|
|
4
|
+
|
|
5
|
+
\`\`\`text
|
|
6
|
+
Ensure you carefully analyze and understand the instructions in @CLAUDE.md.
|
|
7
|
+
I want to create a new project using A3 leveraging this initial "quick start" framework.
|
|
8
|
+
\`\`\`
|
|
9
|
+
|
|
10
|
+
**A3** is a TypeScript framework for building multi-agent (agentic) applications. It's not an autonomous tool or a computer-use assistant, it's a developer framework for orchestrating specialized agents that handle structured data, hand off to each other deterministically, and integrate with any LLM provider (AWS Bedrock, OpenAI, Anthropic, etc).
|
|
11
|
+
|
|
12
|
+
Here's how A3 has been used in production:
|
|
13
|
+
|
|
14
|
+
### Healthcare — Patient Intake & Booking
|
|
15
|
+
|
|
16
|
+
A3 orchestrates a complex patient intake, triage, and booking flow using **16 specialized agents** that handle strictly validated, typed data payloads — gracefully handing off to one another without the confusion or hallucination of a single massive prompt, all backed securely by AWS DynamoDB:
|
|
17
|
+
|
|
18
|
+
- **Pre-Triage Intake** → Verifies identity, routes to Illness or Injury Triage
|
|
19
|
+
- **Illness / Injury Triage** → Assesses symptoms or injury details
|
|
20
|
+
- **Visit Review** → Confirms clinic location
|
|
21
|
+
- **Patient Review** → Validates demographic and clinical records
|
|
22
|
+
- **Insurance Review** → Verifies medical coverage
|
|
23
|
+
- **Payment** → Handles copays and balances
|
|
24
|
+
- **Pick A Time** → Presents available scheduling slots
|
|
25
|
+
- **Book Visit** → Commits the appointment
|
|
26
|
+
- **Pharmacy Method** → Confirms prescription routing
|
|
27
|
+
- **Summary & Wrap-Up** → Final instructions and session close
|
|
28
|
+
|
|
29
|
+
Plus edge-case agents: Rejection Handler (ER reroutes), Urgent Care Guide (FAQs), Find User By Phone (SMS matching), and Survey (post-visit feedback).
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
Ask me anything about building with A3 — agents, transitions, providers, or getting started!
|
|
34
|
+
`
|
|
35
|
+
|
|
36
|
+
export const SESSION_IDS = {
|
|
37
|
+
ONBOARDING: 'onboarding',
|
|
38
|
+
EXAMPLES: {
|
|
39
|
+
BLOCKING: 'blocking',
|
|
40
|
+
STREAMING: 'streaming',
|
|
41
|
+
AGUI: 'agui',
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const EXAMPLE_GREETING = `Hi there! I'm the **greeting agent**. What's your name?`
|
|
46
|
+
|
|
47
|
+
export const SESSION_INITIAL_MESSAGES: Record<string, string> = {
|
|
48
|
+
[SESSION_IDS.ONBOARDING]: WELCOME_MESSAGE_TEXT,
|
|
49
|
+
[SESSION_IDS.EXAMPLES.BLOCKING]: EXAMPLE_GREETING,
|
|
50
|
+
[SESSION_IDS.EXAMPLES.STREAMING]: EXAMPLE_GREETING,
|
|
51
|
+
[SESSION_IDS.EXAMPLES.AGUI]: EXAMPLE_GREETING,
|
|
52
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const REPO_URL = 'https://github.com/generalui/a3/blob/main'
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// --- Metadata ---
|
|
2
|
+
export const APP_TITLE = 'A3 Core Example'
|
|
3
|
+
export const APP_DESCRIPTION = 'Example application for @genui/a3'
|
|
4
|
+
|
|
5
|
+
// --- Navigation (Sidebar) ---
|
|
6
|
+
export const NAV_HEADER = 'A3'
|
|
7
|
+
export const NAV_HOME = 'Home'
|
|
8
|
+
export const NAV_ONBOARDING = 'Onboarding'
|
|
9
|
+
export const NAV_EXAMPLES = 'Examples'
|
|
10
|
+
export const NAV_CHAT = 'Chat'
|
|
11
|
+
export const NAV_STREAMING = 'Streaming'
|
|
12
|
+
export const NAV_AGUI = 'AG-UI'
|
|
13
|
+
|
|
14
|
+
// --- Onboarding ---
|
|
15
|
+
export const ONBOARDING_TAGLINE =
|
|
16
|
+
'<strong>A3</strong> — Predictable, governable multi-agent orchestration for TypeScript.'
|
|
17
|
+
|
|
18
|
+
// --- Examples Index ---
|
|
19
|
+
export const EXAMPLES_HEADING = 'A3 Examples'
|
|
20
|
+
export const EXAMPLES_SUBTITLE =
|
|
21
|
+
'Explore the different communication protocols and frontend implementations available in the A3 architecture.'
|
|
22
|
+
export const EXAMPLES_CTA = 'Try it out'
|
|
23
|
+
|
|
24
|
+
export const EXAMPLE_BLOCKING_TITLE = 'Blocking Chat'
|
|
25
|
+
export const EXAMPLE_BLOCKING_DESCRIPTION =
|
|
26
|
+
"A synchronous (unary) chat implementation. The client waits for the agent to finish processing completely before rendering the response."
|
|
27
|
+
export const EXAMPLE_STREAMING_TITLE = 'Streaming Chat'
|
|
28
|
+
export const EXAMPLE_STREAMING_DESCRIPTION =
|
|
29
|
+
"A streaming response implementation using Server-Sent Events (SSE). The client renders the agent's response incrementally as it's being generated."
|
|
30
|
+
export const EXAMPLE_AGUI_TITLE = 'AG-UI Protocol'
|
|
31
|
+
export const EXAMPLE_AGUI_DESCRIPTION =
|
|
32
|
+
'Agentic UI implementation using the AG-UI protocol. The agent returns structured semantic events driving the client interface in real-time.'
|
|
33
|
+
|
|
34
|
+
// --- Example Page Titles & Descriptions ---
|
|
35
|
+
export const PAGE_BLOCKING_TITLE = 'A3 Example — Blocking Chat'
|
|
36
|
+
export const PAGE_BLOCKING_DESCRIPTION =
|
|
37
|
+
"A greeting agent asks for your name. Once it has it, control passes to an age agent that asks for your age. You can ask to change your name at any time and you'll be handed back to the greeting agent. Each response arrives in full once the agent is done thinking."
|
|
38
|
+
export const PAGE_STREAMING_TITLE = 'A3 Example — Streaming'
|
|
39
|
+
export const PAGE_STREAMING_DESCRIPTION =
|
|
40
|
+
"A greeting agent asks for your name. Once it has it, control passes to an age agent that asks for your age. You can ask to change your name at any time and you'll be handed back to the greeting agent."
|
|
41
|
+
export const PAGE_AGUI_TITLE = 'A3 Example — AG-UI Protocol'
|
|
42
|
+
export const PAGE_AGUI_DESCRIPTION =
|
|
43
|
+
"A greeting agent asks for your name. Once it has it, control passes to an age agent that asks for your age. You can ask to change your name at any time and you'll be handed back to the greeting agent. Communication uses the AG-UI protocol, streaming structured events for text, transitions, and run lifecycle."
|
|
44
|
+
|
|
45
|
+
// --- Chat UI ---
|
|
46
|
+
export const CHAT_PLACEHOLDER = 'Type a message...'
|
|
47
|
+
export const CHAT_SEND = 'Send'
|
|
48
|
+
export const CHAT_RESTART = 'Restart session'
|
|
49
|
+
export const CHAT_ERROR = 'Sorry, something went wrong. Please try again.'
|
|
50
|
+
export const CHAT_ERROR_SHORT = 'Sorry, something went wrong.'
|
|
51
|
+
export const CHAT_TRANSITION = 'Agent transition in progress...'
|
|
52
|
+
|
|
53
|
+
// --- State Viewer ---
|
|
54
|
+
export const STATE_HEADING = 'Session State'
|
|
55
|
+
export const STATE_EMPTY = 'No state properties'
|
|
56
|
+
|
|
57
|
+
// --- Agent Graph ---
|
|
58
|
+
export const GRAPH_HEADING = 'Agent Graph'
|
|
59
|
+
export const GRAPH_LEGEND_DETERMINISTIC = 'Deterministic'
|
|
60
|
+
export const GRAPH_LEGEND_LLM = 'LLM-driven'
|
|
61
|
+
export const GRAPH_ACTIVE_BADGE = 'Active'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getChatSessionInstance } from '@agents'
|
|
2
|
+
import { getAgentGraphData } from '@lib/getAgentGraphData'
|
|
3
|
+
import { initRegistry } from '@agents/registry'
|
|
4
|
+
import { SESSION_IDS } from '@constants/chat'
|
|
5
|
+
import { PAGE_AGUI_TITLE, PAGE_AGUI_DESCRIPTION } from '@constants/ui'
|
|
6
|
+
import { ExamplePageLayout } from '@organisms'
|
|
7
|
+
|
|
8
|
+
export default async function AguiExample() {
|
|
9
|
+
initRegistry()
|
|
10
|
+
const agents = getAgentGraphData()
|
|
11
|
+
const session = getChatSessionInstance({ sessionId: SESSION_IDS.EXAMPLES.AGUI })
|
|
12
|
+
const sessionData = await session.getOrCreateSessionData()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ExamplePageLayout
|
|
16
|
+
title={PAGE_AGUI_TITLE}
|
|
17
|
+
description={PAGE_AGUI_DESCRIPTION}
|
|
18
|
+
sessionId={SESSION_IDS.EXAMPLES.AGUI}
|
|
19
|
+
initialMessages={sessionData.messages}
|
|
20
|
+
initialActiveAgentId={sessionData.activeAgentId}
|
|
21
|
+
initialState={sessionData.state}
|
|
22
|
+
variant="agui"
|
|
23
|
+
agents={agents}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getChatSessionInstance } from '@agents'
|
|
2
|
+
import { getAgentGraphData } from '@lib/getAgentGraphData'
|
|
3
|
+
import { initRegistry } from '@agents/registry'
|
|
4
|
+
import { SESSION_IDS } from '@constants/chat'
|
|
5
|
+
import { PAGE_BLOCKING_TITLE, PAGE_BLOCKING_DESCRIPTION } from '@constants/ui'
|
|
6
|
+
import { ExamplePageLayout } from '@organisms'
|
|
7
|
+
|
|
8
|
+
export default async function ChatExample() {
|
|
9
|
+
initRegistry()
|
|
10
|
+
const agents = getAgentGraphData()
|
|
11
|
+
const session = getChatSessionInstance({ sessionId: SESSION_IDS.EXAMPLES.BLOCKING })
|
|
12
|
+
const sessionData = await session.getOrCreateSessionData()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ExamplePageLayout
|
|
16
|
+
title={PAGE_BLOCKING_TITLE}
|
|
17
|
+
description={PAGE_BLOCKING_DESCRIPTION}
|
|
18
|
+
sessionId={SESSION_IDS.EXAMPLES.BLOCKING}
|
|
19
|
+
initialMessages={sessionData.messages}
|
|
20
|
+
initialActiveAgentId={sessionData.activeAgentId}
|
|
21
|
+
initialState={sessionData.state}
|
|
22
|
+
variant="blocking"
|
|
23
|
+
agents={agents}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import Link from 'next/link'
|
|
4
|
+
import { Typography, Card, CardActionArea, Container, Stack, Box } from '@mui/material'
|
|
5
|
+
import ChatBubbleOutlineIcon from '@mui/icons-material/ChatBubbleOutline'
|
|
6
|
+
import StreamIcon from '@mui/icons-material/Stream'
|
|
7
|
+
import ExtensionIcon from '@mui/icons-material/Extension'
|
|
8
|
+
import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
|
|
9
|
+
import {
|
|
10
|
+
EXAMPLES_HEADING,
|
|
11
|
+
EXAMPLES_SUBTITLE,
|
|
12
|
+
EXAMPLES_CTA,
|
|
13
|
+
EXAMPLE_BLOCKING_TITLE,
|
|
14
|
+
EXAMPLE_BLOCKING_DESCRIPTION,
|
|
15
|
+
EXAMPLE_STREAMING_TITLE,
|
|
16
|
+
EXAMPLE_STREAMING_DESCRIPTION,
|
|
17
|
+
EXAMPLE_AGUI_TITLE,
|
|
18
|
+
EXAMPLE_AGUI_DESCRIPTION,
|
|
19
|
+
} from '@constants/ui'
|
|
20
|
+
|
|
21
|
+
const EXAMPLES = [
|
|
22
|
+
{
|
|
23
|
+
title: EXAMPLE_BLOCKING_TITLE,
|
|
24
|
+
description: EXAMPLE_BLOCKING_DESCRIPTION,
|
|
25
|
+
href: '/examples/chat',
|
|
26
|
+
icon: <ChatBubbleOutlineIcon fontSize="large" />,
|
|
27
|
+
color: '#2563eb',
|
|
28
|
+
bgColor: 'rgba(37, 99, 235, 0.1)',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: EXAMPLE_STREAMING_TITLE,
|
|
32
|
+
description: EXAMPLE_STREAMING_DESCRIPTION,
|
|
33
|
+
href: '/examples/stream',
|
|
34
|
+
icon: <StreamIcon fontSize="large" />,
|
|
35
|
+
color: '#9c27b0',
|
|
36
|
+
bgColor: 'rgba(156, 39, 176, 0.1)',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: EXAMPLE_AGUI_TITLE,
|
|
40
|
+
description: EXAMPLE_AGUI_DESCRIPTION,
|
|
41
|
+
href: '/examples/agui',
|
|
42
|
+
icon: <ExtensionIcon fontSize="large" />,
|
|
43
|
+
color: '#2e7d32',
|
|
44
|
+
bgColor: 'rgba(46, 125, 50, 0.1)',
|
|
45
|
+
},
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
export default function ExamplesIndex() {
|
|
49
|
+
return (
|
|
50
|
+
<Box sx={{ flex: 1, p: 3, overflowY: 'auto' }}>
|
|
51
|
+
<Container maxWidth="lg" sx={{ mt: 5, mb: 10 }}>
|
|
52
|
+
<Stack spacing={3} alignItems="center" mb={10} textAlign="center">
|
|
53
|
+
<Typography variant="h3" fontWeight={800} color="text.primary">
|
|
54
|
+
{EXAMPLES_HEADING}
|
|
55
|
+
</Typography>
|
|
56
|
+
<Typography variant="h6" color="text.secondary" maxWidth="md">
|
|
57
|
+
{EXAMPLES_SUBTITLE}
|
|
58
|
+
</Typography>
|
|
59
|
+
</Stack>
|
|
60
|
+
|
|
61
|
+
<Stack direction={{ xs: 'column', md: 'row' }} spacing={4}>
|
|
62
|
+
{EXAMPLES.map((example) => (
|
|
63
|
+
<Card
|
|
64
|
+
key={example.href}
|
|
65
|
+
elevation={0}
|
|
66
|
+
sx={{
|
|
67
|
+
flex: 1,
|
|
68
|
+
border: 1,
|
|
69
|
+
borderColor: 'divider',
|
|
70
|
+
borderRadius: 4,
|
|
71
|
+
transition: '0.2s',
|
|
72
|
+
'&:hover': { borderColor: example.color, transform: 'translateY(-4px)', boxShadow: 4 },
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<CardActionArea
|
|
76
|
+
component={Link}
|
|
77
|
+
href={example.href}
|
|
78
|
+
sx={{
|
|
79
|
+
p: 4,
|
|
80
|
+
height: '100%',
|
|
81
|
+
display: 'flex',
|
|
82
|
+
flexDirection: 'column',
|
|
83
|
+
alignItems: 'flex-start',
|
|
84
|
+
gap: 2,
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
<Box sx={{ p: 2, bgcolor: example.bgColor, color: example.color, borderRadius: 2 }}>
|
|
88
|
+
{example.icon}
|
|
89
|
+
</Box>
|
|
90
|
+
<Typography variant="h5" fontWeight="bold">
|
|
91
|
+
{example.title}
|
|
92
|
+
</Typography>
|
|
93
|
+
<Typography variant="body1" color="text.secondary" sx={{ flexGrow: 1 }}>
|
|
94
|
+
{example.description}
|
|
95
|
+
</Typography>
|
|
96
|
+
<Stack direction="row" alignItems="center" gap={1} sx={{ color: example.color }} fontWeight="bold" mt={2}>
|
|
97
|
+
{EXAMPLES_CTA} <ArrowForwardIcon fontSize="small" />
|
|
98
|
+
</Stack>
|
|
99
|
+
</CardActionArea>
|
|
100
|
+
</Card>
|
|
101
|
+
))}
|
|
102
|
+
</Stack>
|
|
103
|
+
</Container>
|
|
104
|
+
</Box>
|
|
105
|
+
)
|
|
106
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { getChatSessionInstance } from '@agents'
|
|
2
|
+
import { getAgentGraphData } from '@lib/getAgentGraphData'
|
|
3
|
+
import { initRegistry } from '@agents/registry'
|
|
4
|
+
import { SESSION_IDS } from '@constants/chat'
|
|
5
|
+
import { PAGE_STREAMING_TITLE, PAGE_STREAMING_DESCRIPTION } from '@constants/ui'
|
|
6
|
+
import { ExamplePageLayout } from '@organisms'
|
|
7
|
+
|
|
8
|
+
export default async function StreamExample() {
|
|
9
|
+
initRegistry()
|
|
10
|
+
const agents = getAgentGraphData()
|
|
11
|
+
const session = getChatSessionInstance({ sessionId: SESSION_IDS.EXAMPLES.STREAMING })
|
|
12
|
+
const sessionData = await session.getOrCreateSessionData()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<ExamplePageLayout
|
|
16
|
+
title={PAGE_STREAMING_TITLE}
|
|
17
|
+
description={PAGE_STREAMING_DESCRIPTION}
|
|
18
|
+
sessionId={SESSION_IDS.EXAMPLES.STREAMING}
|
|
19
|
+
initialMessages={sessionData.messages}
|
|
20
|
+
initialActiveAgentId={sessionData.activeAgentId}
|
|
21
|
+
initialState={sessionData.state}
|
|
22
|
+
variant="stream"
|
|
23
|
+
agents={agents}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
|
2
|
+
<defs>
|
|
3
|
+
<style>
|
|
4
|
+
@media (prefers-color-scheme: dark) {
|
|
5
|
+
.logo-mark { fill: #ffffff; }
|
|
6
|
+
}
|
|
7
|
+
</style>
|
|
8
|
+
</defs>
|
|
9
|
+
<g>
|
|
10
|
+
<path class="logo-mark" d="M 560.50 870.11 C544.60,872.39 493.52,873.54 477.50,871.98 C429.64,867.32 381.89,853.57 340.50,832.53 C316.53,820.34 296.91,807.54 274.00,789.15 C261.51,779.12 236.58,754.18 225.88,741.00 C180.72,685.39 153.06,618.33 146.08,547.58 C144.70,533.59 144.69,497.56 146.06,483.42 C151.82,424.12 172.58,365.85 205.33,317.00 C257.37,239.38 349.03,180.93 442.00,166.07 C464.04,162.55 478.67,161.69 505.88,162.29 C555.48,163.38 594.16,171.21 639.00,189.25 C659.38,197.45 694.34,215.75 702.79,222.64 L 705.07 224.50 L 699.18 234.00 C695.94,239.23 690.48,248.11 687.05,253.75 C683.61,259.39 680.44,264.00 679.99,264.00 C679.54,264.00 674.52,261.04 668.84,257.42 C655.11,248.69 625.53,234.22 610.00,228.66 C583.75,219.25 553.35,212.43 527.00,210.05 C512.11,208.70 478.67,208.71 464.00,210.07 C448.87,211.47 421.38,216.98 407.00,221.49 C359.48,236.39 317.14,262.02 281.44,297.50 C258.31,320.48 241.26,343.77 226.14,373.00 C202.42,418.87 191.69,463.62 191.74,516.50 C191.78,555.23 197.10,585.98 210.10,622.48 C215.21,636.83 229.38,665.53 238.40,679.79 C258.58,711.72 284.51,739.87 314.60,762.50 C358.47,795.49 416.85,818.66 475.00,826.15 C490.90,828.19 536.15,827.93 552.00,825.69 C589.08,820.47 619.66,811.35 651.80,795.94 C758.47,744.79 825.34,642.74 828.69,526.00 C830.16,474.91 818.90,425.90 794.39,376.67 C790.88,369.61 788.00,363.68 788.00,363.49 C788.00,362.88 830.91,349.00 832.81,349.00 C834.20,349.00 836.30,352.42 841.26,362.75 C849.43,379.78 850.04,381.23 856.16,398.49 C887.34,486.35 882.15,583.99 841.89,667.00 C819.83,712.49 793.79,746.96 757.00,779.42 C700.79,829.00 634.78,859.47 560.50,870.11 ZM 335.80 648.00 L 331.50 660.50 L 297.25 660.76 C278.41,660.91 263.00,660.77 263.00,660.45 C263.00,659.86 278.07,617.38 291.17,581.00 C295.04,570.28 308.24,533.38 320.52,499.00 C332.80,464.62 347.87,422.55 354.01,405.50 C360.15,388.45 366.62,370.45 368.39,365.50 L 371.61 356.50 L 409.10 356.24 L 446.59 355.98 L 457.22 385.74 C463.07,402.11 475.32,436.42 484.45,462.00 C493.58,487.58 502.36,512.10 503.97,516.50 C505.58,520.90 515.27,547.90 525.51,576.50 C535.75,605.10 546.52,635.12 549.43,643.22 C552.35,651.31 554.49,658.17 554.20,658.47 C553.91,658.76 537.97,659.00 518.78,659.00 L 483.90 659.00 L 482.58 655.75 C481.85,653.96 477.26,640.69 472.38,626.26 L 463.50 600.02 L 352.45 600.00 L 346.28 617.75 C342.88,627.51 338.17,641.12 335.80,648.00 ZM 687.50 661.35 C660.93,665.46 637.94,663.57 615.06,655.41 C601.27,650.49 591.03,644.01 580.37,633.46 C572.36,625.53 570.61,623.14 566.13,613.96 C560.20,601.81 557.14,589.54 556.76,576.32 L 556.50 567.50 L 616.57 566.97 L 617.62 574.74 C620.91,598.97 640.94,614.52 666.50,612.68 C689.18,611.04 704.64,597.72 707.93,576.97 C709.92,564.44 706.02,552.13 697.07,542.67 C687.39,532.43 676.26,529.04 652.23,529.02 L 636.96 529.00 L 637.23 504.75 L 637.50 480.50 L 654.00 479.93 C674.62,479.21 680.48,477.31 690.26,468.16 C697.55,461.34 700.26,455.37 700.77,445.00 C701.35,433.25 698.88,426.59 691.06,418.89 C682.94,410.89 676.70,408.61 663.00,408.61 C649.61,408.61 643.48,410.81 635.00,418.66 C628.14,425.01 625.48,428.98 622.42,437.42 L 620.22 443.50 L 591.93 443.77 C576.36,443.91 563.30,443.69 562.90,443.27 C561.92,442.23 564.65,426.97 567.16,419.51 C578.07,387.00 606.35,365.08 645.92,358.47 C659.23,356.24 682.31,356.90 695.19,359.88 C714.30,364.30 729.13,372.16 741.60,384.49 C755.80,398.54 761.90,412.81 762.75,434.04 C763.17,444.62 762.92,447.89 761.13,455.04 C756.76,472.50 747.16,486.30 733.31,495.04 C729.72,497.32 727.16,499.52 727.64,499.94 C728.11,500.37 731.08,501.76 734.24,503.03 C747.92,508.57 761.40,522.70 767.11,537.47 C771.91,549.87 773.33,559.36 772.69,574.50 C772.04,589.80 770.30,597.27 764.55,609.50 C751.70,636.85 723.57,655.78 687.50,661.35 ZM 370.99 545.75 C371.00,546.73 379.19,547.00 408.61,547.00 C438.28,547.00 446.12,546.74 445.75,545.75 C445.49,545.06 437.02,519.64 426.93,489.25 C416.83,458.86 408.31,434.00 407.99,434.00 C407.33,434.00 370.98,543.79 370.99,545.75 Z" fill="rgb(4,4,4)"/>
|
|
11
|
+
<path d="M 806.90 322.97 C785.45,328.12 765.24,322.48 749.97,307.09 C737.88,294.91 733.00,282.92 733.00,265.40 C733.00,254.57 734.84,247.01 739.72,237.71 C746.39,225.02 759.41,213.62 772.68,208.85 C780.93,205.88 799.11,205.17 807.92,207.47 C830.44,213.34 847.16,231.12 851.67,253.99 C855.45,273.14 849.31,292.70 835.02,307.04 C826.17,315.92 818.73,320.14 806.90,322.97 Z" fill="rgb(248,175,81)"/>
|
|
12
|
+
</g>
|
|
13
|
+
</svg>
|