Skip to content

Commit 5bf9899

Browse files
committed
feat: add in provider and context management for package consumption
1 parent 49646a1 commit 5bf9899

File tree

11 files changed

+1807
-29
lines changed

11 files changed

+1807
-29
lines changed

packages/playground/src/app/layout.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Metadata } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
33
import "./globals.css";
4-
import { Toaster } from "@/components/ui/toaster";
4+
import { PlaygroundProvider } from "@/components/playground-provider";
55

66
const geistSans = Geist({
77
variable: "--font-geist-sans",
@@ -28,8 +28,9 @@ export default function RootLayout({
2828
<body
2929
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
3030
>
31-
{children}
32-
<Toaster />
31+
<PlaygroundProvider>
32+
{children}
33+
</PlaygroundProvider>
3334
</body>
3435
</html>
3536
);

packages/playground/src/components/agent-selector.tsx

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,12 @@ import {
1111
SelectValue,
1212
} from "@/components/ui/select";
1313
import { Badge } from "@/components/ui/badge";
14-
import { type Agent, getAllAgents } from "@/lib/config";
14+
import { type Agent } from "@/lib/config";
1515
import { AddAgentModal } from "./add-agent-modal";
16-
import { type CustomAgent } from "@/lib/agent-storage";
17-
import { useAgentsHealth } from "@/hooks/use-agent-health";
16+
import { useAgentSelection, useAgentManagement } from "@/lib/agent-context";
1817
import { getAgentStatusMessage } from "@/lib/agent-health";
1918

20-
interface AgentSelectorProps {
19+
export interface AgentSelectorProps {
2120
selectedAgent: Agent;
2221
onAgentChange: (agent: Agent) => void;
2322
className?: string;
@@ -28,20 +27,27 @@ export function AgentSelector({
2827
onAgentChange,
2928
className,
3029
}: AgentSelectorProps) {
31-
const [agents, setAgents] = useState<Agent[]>(getAllAgents());
3230
const [isAddModalOpen, setIsAddModalOpen] = useState(false);
3331

34-
// Use health monitoring for all agents
32+
// Use the agent context hooks
3533
const {
36-
health: agentHealth,
37-
isLoading,
38-
error,
39-
} = useAgentsHealth(agents, 30000);
34+
agents,
35+
selectedAgent: contextSelectedAgent,
36+
selectAgent,
37+
} = useAgentSelection();
38+
const { refreshAgents } = useAgentManagement();
39+
40+
// Use the selected agent from props or context (props takes precedence)
41+
const currentAgent = selectedAgent || contextSelectedAgent;
42+
43+
// Refresh agent health on mount
44+
useEffect(() => {
45+
refreshAgents();
46+
// eslint-disable-next-line react-hooks/exhaustive-deps
47+
}, []); // Empty dependency array to run only on mount
4048

4149
// Find health status for selected agent
42-
const selectedAgentHealth = agentHealth.find(
43-
(a) => a.id === selectedAgent.id,
44-
)?.health;
50+
const selectedAgentHealth = currentAgent?.health;
4551
const isConnected = selectedAgentHealth?.isOnline ?? false;
4652

4753
const handleValueChange = (value: string) => {
@@ -50,17 +56,23 @@ export function AgentSelector({
5056
return;
5157
}
5258

53-
const agent = agents.find((a) => a.id === value);
59+
const agent = agents.find((agent) => agent.id === value);
5460
if (agent) {
61+
// Update both the context and call the prop callback
62+
selectAgent(agent.id);
5563
onAgentChange(agent);
5664
}
5765
};
5866

59-
const handleAgentAdded = (newAgent: CustomAgent) => {
60-
// Refresh agents list
61-
setAgents(getAllAgents());
62-
// Automatically select the new agent
63-
onAgentChange(newAgent);
67+
const handleAgentAdded = (newAgent: Agent) => {
68+
// The context will automatically handle the new agent
69+
// Just close the modal and the new agent will appear in the list
70+
setIsAddModalOpen(false);
71+
// Automatically select the new agent if it's online
72+
if (newAgent.health?.isOnline) {
73+
selectAgent(newAgent.id);
74+
onAgentChange(newAgent);
75+
}
6476
};
6577

6678
return (
@@ -78,12 +90,12 @@ export function AgentSelector({
7890
</div>
7991
</div>
8092

81-
<Select value={selectedAgent.id} onValueChange={handleValueChange}>
93+
<Select value={currentAgent?.id} onValueChange={handleValueChange}>
8294
<SelectTrigger className="w-[240px] bg-white dark:bg-gray-800 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-white shadow-sm">
8395
<SelectValue placeholder="Select an agent" />
8496
</SelectTrigger>
8597
<SelectContent className="bg-white dark:bg-gray-800 border-gray-200 dark:border-gray-700">
86-
{agentHealth.map((agent) => (
98+
{agents.map((agent) => (
8799
<SelectItem
88100
key={agent.id}
89101
value={agent.id}

packages/playground/src/components/floating-chat.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export function FloatingChatButton() {
9393
);
9494
}
9595

96-
interface FloatingChatInternalProps {
96+
export interface FloatingChatInternalProps {
9797
isOpen: boolean;
9898
onClose: () => void;
9999
className?: string;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"use client";
2+
3+
import React, { ReactNode } from "react";
4+
import { AgentProvider, AgentProviderProps } from "@/lib/agent-context";
5+
import { Toaster } from "@/components/ui/toaster";
6+
7+
export interface PlaygroundProviderProps extends Omit<AgentProviderProps, "children"> {
8+
children: ReactNode;
9+
showToaster?: boolean;
10+
}
11+
12+
export function PlaygroundProvider({
13+
children,
14+
showToaster = true,
15+
...agentProviderProps
16+
}: PlaygroundProviderProps) {
17+
return (
18+
<AgentProvider {...agentProviderProps}>
19+
{children}
20+
{showToaster && <Toaster />}
21+
</AgentProvider>
22+
);
23+
}
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import React, { ReactNode } from "react"
2+
import { AgentProvider, AgentProviderProps } from "@/lib/agent-context"
3+
import { Agent } from "@/lib/config"
4+
import { getRecommendedAgents } from "@/lib/agent-utils"
5+
6+
/**
7+
* Configuration interface for setting up the agent management system
8+
* This provides a simple, declarative way to configure agents in your application
9+
*/
10+
11+
// Main configuration interface
12+
export interface AgentConfig {
13+
/** Initial agents to load */
14+
agents?: Agent[]
15+
/** Whether to persist agents to localStorage */
16+
persistToLocalStorage?: boolean
17+
/** Whether to load recommended agents if no agents are configured */
18+
useRecommendedAgents?: boolean
19+
/** Custom agent provider props */
20+
providerProps?: Partial<AgentProviderProps>
21+
}
22+
23+
// Props for the main configuration component
24+
export interface AgentConfigProviderProps extends AgentConfig {
25+
children: ReactNode
26+
}
27+
28+
/**
29+
* Main configuration component that sets up the agent management system
30+
* This is the recommended way to configure agents in your application
31+
*/
32+
export function AgentConfigProvider({
33+
children,
34+
agents,
35+
persistToLocalStorage = true,
36+
useRecommendedAgents = true,
37+
providerProps = {},
38+
}: AgentConfigProviderProps) {
39+
// Determine which agents to use
40+
let initialAgents: Agent[]
41+
42+
if (agents && agents.length > 0) {
43+
// Use provided agents
44+
initialAgents = agents
45+
} else if (useRecommendedAgents) {
46+
// Use recommended agents
47+
initialAgents = getRecommendedAgents()
48+
} else {
49+
// Use default agent only
50+
initialAgents = []
51+
}
52+
53+
return (
54+
<AgentProvider
55+
initialAgents={initialAgents}
56+
persistToLocalStorage={persistToLocalStorage}
57+
{...providerProps}
58+
>
59+
{children}
60+
</AgentProvider>
61+
)
62+
}
63+
64+
/**
65+
* Pre-configured agent setups for common scenarios
66+
*/
67+
68+
// Development environment configuration
69+
export function DevelopmentAgentConfig({ children }: { children: ReactNode }) {
70+
return (
71+
<AgentConfigProvider
72+
agents={[
73+
{
74+
id: "dev-local",
75+
name: "Local Development",
76+
url: "http://localhost:8787",
77+
},
78+
{
79+
id: "dev-docker",
80+
name: "Docker Development",
81+
url: "http://localhost:3000",
82+
},
83+
]}
84+
persistToLocalStorage={true}
85+
useRecommendedAgents={false}
86+
>
87+
{children}
88+
</AgentConfigProvider>
89+
)
90+
}
91+
92+
// Production environment configuration
93+
export function ProductionAgentConfig({ children }: { children: ReactNode }) {
94+
return (
95+
<AgentConfigProvider
96+
agents={[
97+
{
98+
id: "prod-primary",
99+
name: "Primary Agent",
100+
url: "https://your-agent.your-domain.com",
101+
},
102+
{
103+
id: "prod-backup",
104+
name: "Backup Agent",
105+
url: "https://backup-agent.your-domain.com",
106+
},
107+
]}
108+
persistToLocalStorage={true}
109+
useRecommendedAgents={false}
110+
>
111+
{children}
112+
</AgentConfigProvider>
113+
)
114+
}
115+
116+
// Minimal configuration (just the default agent)
117+
export function MinimalAgentConfig({ children }: { children: ReactNode }) {
118+
return (
119+
<AgentConfigProvider
120+
agents={[]}
121+
persistToLocalStorage={false}
122+
useRecommendedAgents={false}
123+
>
124+
{children}
125+
</AgentConfigProvider>
126+
)
127+
}
128+
129+
// Configuration with only recommended agents
130+
export function RecommendedAgentConfig({ children }: { children: ReactNode }) {
131+
return (
132+
<AgentConfigProvider
133+
persistToLocalStorage={true}
134+
useRecommendedAgents={true}
135+
>
136+
{children}
137+
</AgentConfigProvider>
138+
)
139+
}
140+
141+
// Testing configuration (no persistence)
142+
export function TestingAgentConfig({ children }: { children: ReactNode }) {
143+
return (
144+
<AgentConfigProvider
145+
agents={[
146+
{
147+
id: "test-agent",
148+
name: "Test Agent",
149+
url: "http://localhost:9999",
150+
},
151+
]}
152+
persistToLocalStorage={false}
153+
useRecommendedAgents={false}
154+
>
155+
{children}
156+
</AgentConfigProvider>
157+
)
158+
}
159+
160+
/**
161+
* Hook for accessing agent configuration
162+
* This can be used to get configuration values in components
163+
*/
164+
export function useAgentConfig() {
165+
// For now, this just returns default values
166+
// In the future, this could read from a configuration context or settings
167+
return {
168+
maxAgents: 10,
169+
healthCheckInterval: 30000, // 30 seconds
170+
connectionTimeout: 5000, // 5 seconds
171+
enableAutoRefresh: true,
172+
enableNotifications: true,
173+
}
174+
}
175+
176+
/**
177+
* Utility functions for configuration management
178+
*/
179+
180+
// Create a configuration from environment variables
181+
export function createConfigFromEnv(): AgentConfig {
182+
const envAgents = process.env.NEXT_PUBLIC_AGENTS
183+
? JSON.parse(process.env.NEXT_PUBLIC_AGENTS)
184+
: undefined
185+
186+
return {
187+
agents: envAgents,
188+
persistToLocalStorage: process.env.NEXT_PUBLIC_PERSIST_AGENTS !== "false",
189+
useRecommendedAgents: process.env.NEXT_PUBLIC_USE_RECOMMENDED_AGENTS !== "false",
190+
}
191+
}
192+
193+
// Validate configuration
194+
export function validateAgentConfig(config: AgentConfig): { isValid: boolean; errors: string[] } {
195+
const errors: string[] = []
196+
197+
if (config.agents && !Array.isArray(config.agents)) {
198+
errors.push("Agents must be an array")
199+
}
200+
201+
if (config.agents) {
202+
config.agents.forEach((agent, index) => {
203+
if (!agent.id) {
204+
errors.push(`Agent at index ${index} is missing an ID`)
205+
}
206+
if (!agent.name) {
207+
errors.push(`Agent at index ${index} is missing a name`)
208+
}
209+
if (!agent.url) {
210+
errors.push(`Agent at index ${index} is missing a URL`)
211+
}
212+
})
213+
}
214+
215+
return {
216+
isValid: errors.length === 0,
217+
errors,
218+
}
219+
}

0 commit comments

Comments
 (0)