harvesthealth commited on
Commit
c2ebec8
·
verified ·
1 Parent(s): 87e4a3f

Upload folder using huggingface_hub

Browse files
.gitignore CHANGED
@@ -1,10 +1,104 @@
1
- # Python build artifacts
2
  __pycache__/
3
- *.pyc
4
- *.pyo
5
- *.pyd
6
 
7
- # Environment folders
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  .env
9
- venv/
10
  env/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
  __pycache__/
3
+ *.py[cod]
4
+ *$py.class
 
5
 
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+ MANIFEST
27
+
28
+ # PyInstaller
29
+ # Usually these files are written by a python script from a template
30
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
31
+ *.manifest
32
+ *.spec
33
+
34
+ # Installer logs
35
+ pip-log.txt
36
+ pip-delete-this-directory.txt
37
+
38
+ # Unit test / coverage reports
39
+ htmlcov/
40
+ .tox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ .hypothesis/
48
+ .pytest_cache/
49
+
50
+ # Translations
51
+ *.mo
52
+ *.pot
53
+
54
+ # Django stuff:
55
+ *.log
56
+ local_settings.py
57
+ db.sqlite3
58
+
59
+ # Flask stuff:
60
+ instance/
61
+ .webassets-cache
62
+
63
+ # Scrapy stuff:
64
+ .scrapy
65
+
66
+ # Sphinx documentation
67
+ docs/_build/
68
+
69
+ # PyBuilder
70
+ target/
71
+
72
+ # Jupyter Notebook
73
+ .ipynb_checkpoints
74
+
75
+ # pyenv
76
+ .python-version
77
+
78
+ # celery beat schedule file
79
+ celerybeat-schedule
80
+
81
+ # SageMath parsed files
82
+ *.sage.py
83
+
84
+ # Environments
85
  .env
86
+ .venv
87
  env/
88
+ venv/
89
+ ENV/
90
+ env.bak/
91
+ venv.bak/
92
+
93
+ # Spyder project settings
94
+ .spyderproject
95
+ .spyderworkspace
96
+
97
+ # Rope project settings
98
+ .ropeproject
99
+
100
+ # mkdocs documentation
101
+ /site
102
+
103
+ # mypy
104
+ .mypy_cache/
App.tsx ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import Sidebar from './components/Sidebar';
3
+ import ChatInterface from './components/ChatInterface';
4
+ import LogMonitor from './components/LogMonitor';
5
+ import ContextManager from './components/ContextManager';
6
+ import { AGENT_CONFIGS } from './constants';
7
+ import { Message, AgentType } from './types';
8
+ import { createJulesSession } from './services/julesService';
9
+
10
+ const App: React.FC = () => {
11
+ const [activeView, setActiveView] = useState('Orchestrator');
12
+ const [messages, setMessages] = useState<Message[]>([
13
+ {
14
+ id: 'welcome',
15
+ sender: AgentType.ORCHESTRATOR,
16
+ content: "Hello! I am your Orchestrator. I can delegate tasks to Jules (Coding), the Developer Agent (Planning), or handle general research. How can I help you today?",
17
+ timestamp: new Date()
18
+ }
19
+ ]);
20
+ const [isProcessing, setIsProcessing] = useState(false);
21
+
22
+ const handleSendMessage = async (content: string) => {
23
+ // 1. Add User Message
24
+ const userMsg: Message = {
25
+ id: Date.now().toString(),
26
+ sender: 'User',
27
+ content,
28
+ timestamp: new Date()
29
+ };
30
+ setMessages(prev => [...prev, userMsg]);
31
+ setIsProcessing(true);
32
+
33
+ // 2. Simulate Orchestrator Logic
34
+ // In a real app, this would be an LLM call to decide delegation
35
+ setTimeout(async () => {
36
+ let responseContent = "";
37
+
38
+ const lowerContent = content.toLowerCase();
39
+
40
+ if (lowerContent.includes("plan") || lowerContent.includes("idea")) {
41
+ // Delegate to Developer Agent
42
+ responseContent = "I'll ask the **Developer Agent** to start planning this architecture for you. I'm initializing the A2A connection to `agent-zero-http`...";
43
+
44
+ // Simulate Dev Agent thinking
45
+ setTimeout(() => {
46
+ const devMsg: Message = {
47
+ id: Date.now().toString() + 'dev',
48
+ sender: AgentType.DEVELOPER,
49
+ content: `I've analyzed the request. Based on the "Example MCP Server Configuration", I will outline a plan using the git-agent.\n\n**Plan:**\n1. Clone GitHub Repo\n2. Analyze structure\n3. Create /deployment file\n\nWaiting for confirmation to execute on HuggingFace Space...`,
50
+ timestamp: new Date()
51
+ };
52
+ setMessages(prev => [...prev, devMsg]);
53
+ }, 1500);
54
+
55
+ } else if (lowerContent.includes("code") || lowerContent.includes("jules") || lowerContent.includes("fix")) {
56
+ // Delegate to Jules
57
+ responseContent = "I'm delegating this coding task to **Jules**. I'm initiating a new Jules API session using key `AQ.Ab8RN...`.";
58
+
59
+ // Simulate Jules Session Creation
60
+ try {
61
+ await createJulesSession("Auto-Generated Task", content, "owner/repo");
62
+ setTimeout(() => {
63
+ const julesMsg: Message = {
64
+ id: Date.now().toString() + 'jules',
65
+ sender: AgentType.JULES,
66
+ content: `Session Created.\nSource: GitHub\nContext: /deployment detected.\n\nI am now analyzing the code. I will report back when the PR is ready.`,
67
+ timestamp: new Date()
68
+ };
69
+ setMessages(prev => [...prev, julesMsg]);
70
+ }, 2000);
71
+ } catch (e) {
72
+ console.error(e);
73
+ }
74
+
75
+ } else if (lowerContent.includes("monitor") || lowerContent.includes("log")) {
76
+ responseContent = "I will alert the **Monitoring Agent** to track the Jules session and HuggingFace build logs. Switching view to monitoring...";
77
+ setTimeout(() => setActiveView('monitoring'), 1500);
78
+ } else {
79
+ responseContent = "I've noted that. Is this a coding task for Jules or a planning task for the Developer agent?";
80
+ }
81
+
82
+ const orchestratorMsg: Message = {
83
+ id: (Date.now() + 1).toString(),
84
+ sender: AgentType.ORCHESTRATOR,
85
+ content: responseContent,
86
+ timestamp: new Date()
87
+ };
88
+
89
+ setMessages(prev => [...prev, orchestratorMsg]);
90
+ setIsProcessing(false);
91
+ }, 1000);
92
+ };
93
+
94
+ const renderContent = () => {
95
+ switch (activeView) {
96
+ case 'monitoring':
97
+ return <LogMonitor />;
98
+ case 'context':
99
+ return <ContextManager />;
100
+ case 'Orchestrator':
101
+ default:
102
+ // For the demo, other agents also redirect to the main chat but we could filter messages
103
+ return (
104
+ <ChatInterface
105
+ messages={messages}
106
+ onSendMessage={handleSendMessage}
107
+ isProcessing={isProcessing}
108
+ />
109
+ );
110
+ }
111
+ };
112
+
113
+ return (
114
+ <div className="flex h-screen bg-black text-gray-100 font-sans">
115
+ <Sidebar
116
+ agents={AGENT_CONFIGS}
117
+ activeView={activeView}
118
+ setActiveView={setActiveView}
119
+ />
120
+ <main className="flex-1 p-4 bg-gray-950 overflow-hidden relative">
121
+ <div className="absolute inset-0 bg-grid-pattern opacity-5 pointer-events-none"></div>
122
+ {renderContent()}
123
+ </main>
124
+ </div>
125
+ );
126
+ };
127
+
128
+ export default App;
README.md CHANGED
@@ -1,12 +1,20 @@
1
- ---
2
- title: Desk Agent
3
- emoji: 🌍
4
- colorFrom: purple
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 6.3.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
1
+ <div align="center">
2
+ <img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
3
+ </div>
4
+
5
+ # Run and deploy your AI Studio app
6
+
7
+ This contains everything you need to run your app locally.
8
+
9
+ View your app in AI Studio: https://ai.studio/apps/drive/1cOgxcY9XRXwQoIScAsa5h9uFcPwztMM1
10
+
11
+ ## Run Locally
12
+
13
+ **Prerequisites:** Node.js
14
+
15
+
16
+ 1. Install dependencies:
17
+ `npm install`
18
+ 2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
19
+ 3. Run the app:
20
+ `npm run dev`
app.py CHANGED
@@ -21,8 +21,10 @@ def stream_orchestrator(msg, history, jules_key, hf_token, blablador_key):
21
  A "pure" generator that only handles streaming UI updates.
22
  It returns the data needed for the background task.
23
  """
24
- history.append([msg, ""])
25
- yield history, None # Yield initial history, monitor_data is None for now
 
 
26
 
27
  api_keys = {
28
  "JULES_API_KEY": os.environ.get("JULES_API_KEY", jules_key),
@@ -31,7 +33,8 @@ def stream_orchestrator(msg, history, jules_key, hf_token, blablador_key):
31
  }
32
  for key, value in api_keys.items():
33
  if not value:
34
- history[-1][1] = f"Error: {key} is not set. Please provide it as a Space secret or in the UI."
 
35
  yield history, None
36
  return
37
  os.environ[key] = value
@@ -39,12 +42,12 @@ def stream_orchestrator(msg, history, jules_key, hf_token, blablador_key):
39
  workflow_generator = main_orchestration_workflow()
40
  monitor_data = None
41
  final_bot_message = ""
42
-
43
  try:
44
- while True:
45
- update = next(workflow_generator)
46
  final_bot_message += update
47
- history[-1][1] = final_bot_message
48
  yield history, None
49
  except StopIteration as e:
50
  monitor_data = e.value
@@ -53,10 +56,11 @@ def stream_orchestrator(msg, history, jules_key, hf_token, blablador_key):
53
  if key in os.environ:
54
  del os.environ[key]
55
 
 
56
  if monitor_data:
57
- history[-1][1] = final_bot_message + "\n\n**Orchestration complete. Starting background monitoring...**"
58
  else:
59
- history[-1][1] = final_bot_message + "\n\n**Workflow finished with an error. Could not start monitor.**"
60
 
61
  yield history, monitor_data
62
 
@@ -96,13 +100,13 @@ with gr.Blocks(title="Desk Agent") as demo:
96
 
97
  chatbot = gr.Chatbot(label="Agent Conversation", height=500)
98
  msg_input = gr.Textbox(label="Your Message", placeholder="Press 'Start Workflow' to begin...")
99
-
100
  with gr.Accordion("API Key Overrides (Optional)", open=False):
101
  gr.Markdown("API keys will be automatically loaded from Space secrets if available.")
102
  jules_key_input = gr.Textbox(label="Jules API Key", type="password")
103
  hf_token_input = gr.Textbox(label="Hugging Face Token", type="password")
104
  blablador_key_input = gr.Textbox(label="Blablador API Key", type="password")
105
-
106
  start_button = gr.Button("Start Workflow", variant="primary")
107
  clear_button = gr.ClearButton([msg_input, chatbot])
108
 
 
21
  A "pure" generator that only handles streaming UI updates.
22
  It returns the data needed for the background task.
23
  """
24
+ # Append user message and initial empty bot message
25
+ history.append({"role": "user", "content": msg})
26
+ history.append({"role": "assistant", "content": ""})
27
+ yield history, None # Yield initial history
28
 
29
  api_keys = {
30
  "JULES_API_KEY": os.environ.get("JULES_API_KEY", jules_key),
 
33
  }
34
  for key, value in api_keys.items():
35
  if not value:
36
+ error_msg = f"Error: {key} is not set. Please provide it as a Space secret or in the UI."
37
+ history[-1]["content"] = error_msg # Update bot message with error
38
  yield history, None
39
  return
40
  os.environ[key] = value
 
42
  workflow_generator = main_orchestration_workflow()
43
  monitor_data = None
44
  final_bot_message = ""
45
+
46
  try:
47
+ # Stream updates to the bot's message (the last item in history)
48
+ for update in workflow_generator:
49
  final_bot_message += update
50
+ history[-1]["content"] = final_bot_message
51
  yield history, None
52
  except StopIteration as e:
53
  monitor_data = e.value
 
56
  if key in os.environ:
57
  del os.environ[key]
58
 
59
+ # Append final status to the bot's message
60
  if monitor_data:
61
+ history[-1]["content"] = final_bot_message + "\n\n**Orchestration complete. Starting background monitoring...**"
62
  else:
63
+ history[-1]["content"] = final_bot_message + "\n\n**Workflow finished with an error. Could not start monitor.**"
64
 
65
  yield history, monitor_data
66
 
 
100
 
101
  chatbot = gr.Chatbot(label="Agent Conversation", height=500)
102
  msg_input = gr.Textbox(label="Your Message", placeholder="Press 'Start Workflow' to begin...")
103
+
104
  with gr.Accordion("API Key Overrides (Optional)", open=False):
105
  gr.Markdown("API keys will be automatically loaded from Space secrets if available.")
106
  jules_key_input = gr.Textbox(label="Jules API Key", type="password")
107
  hf_token_input = gr.Textbox(label="Hugging Face Token", type="password")
108
  blablador_key_input = gr.Textbox(label="Blablador API Key", type="password")
109
+
110
  start_button = gr.Button("Start Workflow", variant="primary")
111
  clear_button = gr.ClearButton([msg_input, chatbot])
112
 
components/ChatInterface.tsx ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useRef, useEffect } from 'react';
2
+ import { Message, AgentType } from '../types';
3
+ import { Send, User, Bot, Loader2 } from 'lucide-react';
4
+
5
+ interface ChatInterfaceProps {
6
+ messages: Message[];
7
+ onSendMessage: (content: string) => void;
8
+ isProcessing: boolean;
9
+ }
10
+
11
+ const ChatInterface: React.FC<ChatInterfaceProps> = ({ messages, onSendMessage, isProcessing }) => {
12
+ const [input, setInput] = useState('');
13
+ const messagesEndRef = useRef<HTMLDivElement>(null);
14
+
15
+ const scrollToBottom = () => {
16
+ messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
17
+ };
18
+
19
+ useEffect(() => {
20
+ scrollToBottom();
21
+ }, [messages]);
22
+
23
+ const handleSubmit = (e: React.FormEvent) => {
24
+ e.preventDefault();
25
+ if (!input.trim() || isProcessing) return;
26
+ onSendMessage(input);
27
+ setInput('');
28
+ };
29
+
30
+ const renderMessageIcon = (sender: string) => {
31
+ if (sender === 'User') return <User className="w-5 h-5 text-gray-300" />;
32
+ return <Bot className="w-5 h-5 text-blue-400" />;
33
+ };
34
+
35
+ return (
36
+ <div className="flex flex-col h-full bg-gray-950 rounded-xl overflow-hidden shadow-2xl border border-gray-800">
37
+ {/* Header */}
38
+ <div className="p-4 border-b border-gray-800 bg-gray-900/50 backdrop-blur">
39
+ <h2 className="text-lg font-semibold text-white">Orchestration Chat</h2>
40
+ <p className="text-xs text-gray-400">Interacting with Orchestrator Agent (Memory Active)</p>
41
+ </div>
42
+
43
+ {/* Messages */}
44
+ <div className="flex-1 overflow-y-auto p-4 space-y-6">
45
+ {messages.map((msg) => (
46
+ <div
47
+ key={msg.id}
48
+ className={`flex gap-3 ${msg.sender === 'User' ? 'flex-row-reverse' : 'flex-row'}`}
49
+ >
50
+ <div className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center ${
51
+ msg.sender === 'User' ? 'bg-gray-700' : 'bg-blue-900/30'
52
+ }`}>
53
+ {renderMessageIcon(msg.sender)}
54
+ </div>
55
+
56
+ <div className={`max-w-[80%] rounded-lg p-3 text-sm leading-relaxed ${
57
+ msg.sender === 'User'
58
+ ? 'bg-gray-800 text-white'
59
+ : 'bg-blue-900/10 text-gray-200 border border-blue-900/30'
60
+ }`}>
61
+ <div className="flex items-center gap-2 mb-1">
62
+ <span className="font-bold text-xs opacity-70">{msg.sender}</span>
63
+ <span className="text-[10px] opacity-40">{msg.timestamp.toLocaleTimeString()}</span>
64
+ </div>
65
+ <div className="whitespace-pre-wrap">{msg.content}</div>
66
+ </div>
67
+ </div>
68
+ ))}
69
+ {isProcessing && (
70
+ <div className="flex items-center gap-2 text-gray-500 text-sm pl-12">
71
+ <Loader2 className="w-4 h-4 animate-spin" />
72
+ <span>Orchestrator is delegating...</span>
73
+ </div>
74
+ )}
75
+ <div ref={messagesEndRef} />
76
+ </div>
77
+
78
+ {/* Input */}
79
+ <div className="p-4 bg-gray-900 border-t border-gray-800">
80
+ <form onSubmit={handleSubmit} className="relative">
81
+ <input
82
+ type="text"
83
+ value={input}
84
+ onChange={(e) => setInput(e.target.value)}
85
+ placeholder="Describe your task (e.g., 'Plan a new health app' or 'Ask Jules to fix the bug')..."
86
+ className="w-full bg-gray-800 border-gray-700 text-white rounded-lg pl-4 pr-12 py-3 focus:outline-none focus:ring-2 focus:ring-blue-600 focus:border-transparent placeholder-gray-500"
87
+ />
88
+ <button
89
+ type="submit"
90
+ disabled={!input.trim() || isProcessing}
91
+ className="absolute right-2 top-2 p-1.5 bg-blue-600 hover:bg-blue-500 text-white rounded-md disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
92
+ >
93
+ <Send className="w-5 h-5" />
94
+ </button>
95
+ </form>
96
+ </div>
97
+ </div>
98
+ );
99
+ };
100
+
101
+ export default ChatInterface;
components/ContextManager.tsx ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import { INITIAL_CONTEXT_PLACEHOLDERS } from '../constants';
3
+ import { Save, FileText } from 'lucide-react';
4
+
5
+ const ContextManager: React.FC = () => {
6
+ const [contexts, setContexts] = useState(INITIAL_CONTEXT_PLACEHOLDERS);
7
+ const [activeTab, setActiveTab] = useState<keyof typeof INITIAL_CONTEXT_PLACEHOLDERS>('julesContext');
8
+
9
+ const handleChange = (key: keyof typeof INITIAL_CONTEXT_PLACEHOLDERS, value: string) => {
10
+ setContexts(prev => ({ ...prev, [key]: value }));
11
+ };
12
+
13
+ const tabs = [
14
+ { key: 'julesContext', label: 'Jules Context' },
15
+ { key: 'devPlanContext', label: 'Dev Plan Context' },
16
+ { key: 'deploymentContext', label: 'Deployment Context' },
17
+ { key: 'monitoringContext', label: 'Monitoring Context' },
18
+ ];
19
+
20
+ return (
21
+ <div className="bg-gray-900 rounded-xl border border-gray-800 h-full flex flex-col overflow-hidden">
22
+ <div className="p-4 border-b border-gray-800 flex justify-between items-center">
23
+ <div>
24
+ <h2 className="text-lg font-bold text-white">Context Management</h2>
25
+ <p className="text-sm text-gray-400">Manage context files injected into agents.</p>
26
+ </div>
27
+ <button className="flex items-center gap-2 px-3 py-1.5 bg-green-600/20 text-green-400 border border-green-600/30 rounded-md hover:bg-green-600/30 transition-colors text-sm">
28
+ <Save className="w-4 h-4" />
29
+ <span>Save All</span>
30
+ </button>
31
+ </div>
32
+
33
+ <div className="flex border-b border-gray-800 overflow-x-auto">
34
+ {tabs.map(tab => (
35
+ <button
36
+ key={tab.key}
37
+ onClick={() => setActiveTab(tab.key as any)}
38
+ className={`px-4 py-3 text-sm font-medium flex items-center gap-2 transition-colors whitespace-nowrap ${
39
+ activeTab === tab.key
40
+ ? 'text-white border-b-2 border-blue-500 bg-gray-800'
41
+ : 'text-gray-400 hover:text-white hover:bg-gray-800/50'
42
+ }`}
43
+ >
44
+ <FileText className="w-4 h-4" />
45
+ {tab.label}
46
+ </button>
47
+ ))}
48
+ </div>
49
+
50
+ <div className="flex-1 p-0 relative">
51
+ <textarea
52
+ value={contexts[activeTab]}
53
+ onChange={(e) => handleChange(activeTab, e.target.value)}
54
+ className="w-full h-full bg-gray-950 text-gray-300 p-4 font-mono text-sm resize-none focus:outline-none"
55
+ spellCheck={false}
56
+ />
57
+ <div className="absolute bottom-4 right-4 text-xs text-gray-600 pointer-events-none">
58
+ Markdown Supported
59
+ </div>
60
+ </div>
61
+ </div>
62
+ );
63
+ };
64
+
65
+ export default ContextManager;
components/LogMonitor.tsx ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from 'react';
2
+ import { MonitoringLog } from '../types';
3
+ import { Terminal, AlertTriangle, CheckCircle, Clock, FileText } from 'lucide-react';
4
+
5
+ const LogMonitor: React.FC = () => {
6
+ const [logs, setLogs] = useState<MonitoringLog[]>([]);
7
+ const [summary, setSummary] = useState<string>("Waiting for logs to generate summary...");
8
+ const [isMonitoring, setIsMonitoring] = useState(false);
9
+
10
+ // Simulate incoming logs
11
+ useEffect(() => {
12
+ let interval: ReturnType<typeof setInterval>;
13
+
14
+ if (isMonitoring) {
15
+ interval = setInterval(() => {
16
+ const sources: ('JulesAPI' | 'HuggingFace' | 'System')[] = ['JulesAPI', 'HuggingFace', 'System'];
17
+ const levels: ('INFO' | 'WARN' | 'ERROR')[] = ['INFO', 'INFO', 'INFO', 'WARN', 'INFO']; // Mostly info
18
+
19
+ const randomSource = sources[Math.floor(Math.random() * sources.length)];
20
+ const randomLevel = levels[Math.floor(Math.random() * levels.length)];
21
+
22
+ const newLog: MonitoringLog = {
23
+ id: Math.random().toString(36),
24
+ timestamp: new Date().toISOString(),
25
+ source: randomSource,
26
+ level: randomLevel,
27
+ message: generateMockLogMessage(randomSource, randomLevel)
28
+ };
29
+
30
+ setLogs(prev => {
31
+ const newLogs = [newLog, ...prev].slice(0, 100);
32
+ updateSummary(newLogs); // Trigger summary update
33
+ return newLogs;
34
+ });
35
+ }, 3000);
36
+ }
37
+
38
+ return () => clearInterval(interval);
39
+ }, [isMonitoring]);
40
+
41
+ const generateMockLogMessage = (source: string, level: string) => {
42
+ if (source === 'JulesAPI') {
43
+ return level === 'ERROR'
44
+ ? 'Session ID #8492 connection timeout.'
45
+ : 'Received activity update: Code generation in progress.';
46
+ }
47
+ if (source === 'HuggingFace') {
48
+ return level === 'WARN'
49
+ ? 'Build container utilizing 85% memory.'
50
+ : 'Successfully pulled layer sha256:e7c96db...';
51
+ }
52
+ return 'Health check ping passed.';
53
+ };
54
+
55
+ const updateSummary = (currentLogs: MonitoringLog[]) => {
56
+ const errors = currentLogs.filter(l => l.level === 'ERROR').length;
57
+ const warns = currentLogs.filter(l => l.level === 'WARN').length;
58
+ const latest = currentLogs[0];
59
+
60
+ setSummary(`Status: ${errors > 0 ? 'Issues Detected' : 'Healthy'}.
61
+ Found ${errors} errors and ${warns} warnings in the last batch.
62
+ Latest activity from ${latest?.source || 'System'}: ${latest?.message || 'None'}.
63
+ Sending condensed report to Jules...`);
64
+ };
65
+
66
+ return (
67
+ <div className="bg-gray-900 rounded-xl border border-gray-800 h-full flex flex-col">
68
+ <div className="p-4 border-b border-gray-800 flex justify-between items-center bg-gray-900/50">
69
+ <div className="flex items-center gap-3">
70
+ <div className="p-2 bg-gray-800 rounded-lg">
71
+ <Terminal className="w-5 h-5 text-emerald-400" />
72
+ </div>
73
+ <div>
74
+ <h2 className="text-lg font-bold text-white">Live Monitor</h2>
75
+ <div className="flex items-center gap-2 text-xs text-gray-400">
76
+ <span>Monitoring Agent Active</span>
77
+ <span className="w-1 h-1 bg-gray-500 rounded-full"></span>
78
+ <span>Polling Interval: 10s</span>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ <button
83
+ onClick={() => setIsMonitoring(!isMonitoring)}
84
+ className={`px-4 py-2 rounded-lg text-sm font-medium transition-colors ${
85
+ isMonitoring
86
+ ? 'bg-red-500/10 text-red-400 border border-red-500/20 hover:bg-red-500/20'
87
+ : 'bg-emerald-500/10 text-emerald-400 border border-emerald-500/20 hover:bg-emerald-500/20'
88
+ }`}
89
+ >
90
+ {isMonitoring ? 'Stop Monitoring' : 'Start Monitoring'}
91
+ </button>
92
+ </div>
93
+
94
+ {/* Summary Panel */}
95
+ <div className="bg-blue-900/10 border-b border-blue-900/20 p-4">
96
+ <div className="flex items-center gap-2 mb-2">
97
+ <FileText className="w-4 h-4 text-blue-400" />
98
+ <h3 className="text-sm font-semibold text-blue-300">Agent Summary (For Jules)</h3>
99
+ </div>
100
+ <p className="text-sm text-gray-300 font-mono leading-relaxed whitespace-pre-line">
101
+ {isMonitoring ? summary : "Start monitoring to generate summaries..."}
102
+ </p>
103
+ </div>
104
+
105
+ <div className="flex-1 overflow-y-auto p-2 font-mono text-sm bg-black/50 space-y-1">
106
+ {logs.length === 0 ? (
107
+ <div className="h-full flex flex-col items-center justify-center text-gray-500 gap-2">
108
+ <Clock className="w-8 h-8 opacity-50" />
109
+ <p>Waiting for logs stream...</p>
110
+ </div>
111
+ ) : (
112
+ logs.map((log) => (
113
+ <div key={log.id} className="flex gap-3 hover:bg-white/5 p-1.5 rounded transition-colors group">
114
+ <span className="text-gray-500 text-xs w-32 shrink-0">{log.timestamp.split('T')[1].slice(0, 12)}</span>
115
+ <span className={`text-xs font-bold w-24 shrink-0 uppercase ${
116
+ log.source === 'JulesAPI' ? 'text-blue-400' :
117
+ log.source === 'HuggingFace' ? 'text-yellow-400' : 'text-purple-400'
118
+ }`}>
119
+ {log.source}
120
+ </span>
121
+ <span className={`text-xs font-bold w-12 shrink-0 ${
122
+ log.level === 'ERROR' ? 'text-red-500' :
123
+ log.level === 'WARN' ? 'text-orange-400' : 'text-gray-400'
124
+ }`}>
125
+ [{log.level}]
126
+ </span>
127
+ <span className="text-gray-300 break-all">{log.message}</span>
128
+ </div>
129
+ ))
130
+ )}
131
+ </div>
132
+
133
+ <div className="p-3 border-t border-gray-800 bg-gray-900 text-xs text-gray-500 flex justify-between">
134
+ <span>Connected to gradio_logsview</span>
135
+ <span>Mode: Summarization</span>
136
+ </div>
137
+ </div>
138
+ );
139
+ };
140
+
141
+ export default LogMonitor;
components/Sidebar.tsx ADDED
@@ -0,0 +1,89 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import { AgentConfig, AgentType } from '../types';
3
+ import { Bot, Code2, BrainCircuit, Activity, Settings, FileText } from 'lucide-react';
4
+
5
+ interface SidebarProps {
6
+ agents: AgentConfig[];
7
+ activeView: string;
8
+ setActiveView: (view: string) => void;
9
+ }
10
+
11
+ const Sidebar: React.FC<SidebarProps> = ({ agents, activeView, setActiveView }) => {
12
+
13
+ const getIcon = (type: AgentType) => {
14
+ switch (type) {
15
+ case AgentType.ORCHESTRATOR: return <BrainCircuit className="w-5 h-5" />;
16
+ case AgentType.JULES: return <Code2 className="w-5 h-5" />;
17
+ case AgentType.DEVELOPER: return <Settings className="w-5 h-5" />;
18
+ case AgentType.GENERAL: return <Bot className="w-5 h-5" />;
19
+ default: return <Bot className="w-5 h-5" />;
20
+ }
21
+ };
22
+
23
+ return (
24
+ <div className="w-64 bg-gray-900 border-r border-gray-800 h-screen flex flex-col p-4">
25
+ <div className="flex items-center gap-2 mb-8 px-2">
26
+ <div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
27
+ <span className="font-bold text-white">O4</span>
28
+ </div>
29
+ <h1 className="text-xl font-bold tracking-tight text-white">Orchestrator</h1>
30
+ </div>
31
+
32
+ <div className="space-y-1 mb-6">
33
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 px-2">Agents</h3>
34
+ {agents.map((agent) => (
35
+ <button
36
+ key={agent.name}
37
+ onClick={() => setActiveView(agent.name)}
38
+ className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm ${
39
+ activeView === agent.name
40
+ ? 'bg-blue-600/10 text-blue-400 border border-blue-600/20'
41
+ : 'text-gray-400 hover:bg-gray-800 hover:text-gray-200'
42
+ }`}
43
+ >
44
+ {getIcon(agent.type)}
45
+ <div className="flex flex-col items-start">
46
+ <span>{agent.name}</span>
47
+ <span className="text-[10px] text-gray-600">{agent.status}</span>
48
+ </div>
49
+ </button>
50
+ ))}
51
+ </div>
52
+
53
+ <div className="space-y-1">
54
+ <h3 className="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2 px-2">System</h3>
55
+ <button
56
+ onClick={() => setActiveView('monitoring')}
57
+ className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm ${
58
+ activeView === 'monitoring'
59
+ ? 'bg-emerald-600/10 text-emerald-400 border border-emerald-600/20'
60
+ : 'text-gray-400 hover:bg-gray-800 hover:text-gray-200'
61
+ }`}
62
+ >
63
+ <Activity className="w-5 h-5" />
64
+ <span>Monitoring</span>
65
+ </button>
66
+ <button
67
+ onClick={() => setActiveView('context')}
68
+ className={`w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors text-sm ${
69
+ activeView === 'context'
70
+ ? 'bg-purple-600/10 text-purple-400 border border-purple-600/20'
71
+ : 'text-gray-400 hover:bg-gray-800 hover:text-gray-200'
72
+ }`}
73
+ >
74
+ <FileText className="w-5 h-5" />
75
+ <span>Context Data</span>
76
+ </button>
77
+ </div>
78
+
79
+ <div className="mt-auto pt-4 border-t border-gray-800">
80
+ <div className="flex items-center gap-2 px-2">
81
+ <div className="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div>
82
+ <span className="text-xs text-gray-500">System Online</span>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ );
87
+ };
88
+
89
+ export default Sidebar;
constants.ts ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { AgentType, AgentConfig } from './types';
2
+
3
+ // NOTE: In a production environment, keys should be in env variables.
4
+ // Using provided keys for the generated artifact as requested.
5
+ export const JULES_API_KEY = "AQ.Ab8RN6JVE9dY_3MhEkAuV3XqZUPXP0jVxG8MjNEJSFDdg6QmsQ";
6
+ export const JULES_API_BASE = "https://jules.googleapis.com/v1alpha";
7
+
8
+ export const AGENT_CONFIGS: AgentConfig[] = [
9
+ {
10
+ name: "Orchestrator",
11
+ type: AgentType.ORCHESTRATOR,
12
+ description: "Secretary-like coordinator with memory. Delegates tasks.",
13
+ status: 'idle'
14
+ },
15
+ {
16
+ name: "Jules",
17
+ type: AgentType.JULES,
18
+ endpoint: JULES_API_BASE,
19
+ description: "Specialized coding agent. Works on GitHub context.",
20
+ status: 'idle'
21
+ },
22
+ {
23
+ name: "Agent-Zero (Dev)",
24
+ type: AgentType.DEVELOPER,
25
+ endpoint: "https://harvesthealth-agent-with-cluade-skills.hf.space/mcp/t-zbg1GXJPryStqwCM/sse",
26
+ description: "Plans coding projects and architecture.",
27
+ status: 'idle'
28
+ },
29
+ {
30
+ name: "General Agent",
31
+ type: AgentType.GENERAL,
32
+ endpoint: "https://leon4gr45-agent-mcp-new-standard.hf.space/a2a/t-VeVubvblNRxNLxSp",
33
+ description: "Non-coding tasks (Research, Notion, DB).",
34
+ status: 'idle'
35
+ }
36
+ ];
37
+
38
+ export const INITIAL_CONTEXT_PLACEHOLDERS = {
39
+ julesContext: `## Jules Agent Context
40
+ - **Role**: Coding Specialist
41
+ - **Integration**: GitHub API (Requires Token)
42
+ - **Constraint**: Must use specific prompt format for remote repo access.
43
+ - **Workflow**: Receive plan -> Implement in Branch -> Auto Create PR.`,
44
+
45
+ devPlanContext: `## Developer Agent Context (Planning)
46
+ - **Role**: Architect & Planner
47
+ - **Output**: JSON/Markdown deployment plans.
48
+ - **Resources**: HuggingFace Deployment Sheets, Log Retrieval Sheets.
49
+ - **Task**: Analyze cloned repo, create /deployment file.`,
50
+
51
+ deploymentContext: `## Deployment & Infrastructure Context
52
+ - **Target**: HuggingFace Spaces.
53
+ - **Tools**: git-agent, hf_transfer.
54
+ - **Secrets**: HF_TOKEN, GITHUB_TOKEN.
55
+ - **Process**:
56
+ 1. Clone Repo
57
+ 2. Add Dockerfile/Requirements
58
+ 3. Push to Space`,
59
+
60
+ monitoringContext: `## Monitoring Agent Context
61
+ - **Target**: Jules Sessions & HF Runtime Logs.
62
+ - **Frequency**: Every 10 minutes.
63
+ - **Tool**: gradio_logsview for Python/Command logs.
64
+ - **Action**: Analyze raw logs and generate a **concise summary** of errors and status.
65
+ - **Constraint**: Do NOT copy-paste raw logs. Summarize findings for Jules API to optimize token usage.`
66
+ };
deployment/idea.md ADDED
File without changes
deployment/jules_prompt_template.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Based on the plan from the developer agent, please implement the following in the `{repository_name}` repository.
2
+
3
+ **High-Level Idea:**
4
+ {high_level_idea}
5
+
6
+ **Detailed Tasks:**
7
+ {detailed_tasks}
8
+
9
+ **Relevant Context Files:**
10
+ {context_files}
11
+
12
+ Please proceed with the implementation. A monitoring agent will be observing the deployment logs for any errors.
deployment/tasks.md ADDED
File without changes
index.html ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Orchestrator-4 Agent System</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <style>
9
+ /* Custom scrollbar for a cleaner UI */
10
+ ::-webkit-scrollbar {
11
+ width: 8px;
12
+ height: 8px;
13
+ }
14
+ ::-webkit-scrollbar-track {
15
+ background: #1f2937;
16
+ }
17
+ ::-webkit-scrollbar-thumb {
18
+ background: #4b5563;
19
+ border-radius: 4px;
20
+ }
21
+ ::-webkit-scrollbar-thumb:hover {
22
+ background: #6b7280;
23
+ }
24
+ </style>
25
+ <script type="importmap">
26
+ {
27
+ "imports": {
28
+ "react": "https://esm.sh/react@^19.2.3",
29
+ "react-dom/": "https://esm.sh/react-dom@^19.2.3/",
30
+ "react/": "https://esm.sh/react@^19.2.3/",
31
+ "lucide-react": "https://esm.sh/lucide-react@^0.562.0"
32
+ }
33
+ }
34
+ </script>
35
+ </head>
36
+ <body class="bg-gray-900 text-gray-100 font-sans antialiased overflow-hidden">
37
+ <div id="root"></div>
38
+ </body>
39
+ </html>
index.tsx ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+
5
+ const rootElement = document.getElementById('root');
6
+ if (!rootElement) {
7
+ throw new Error("Could not find root element to mount to");
8
+ }
9
+
10
+ const root = ReactDOM.createRoot(rootElement);
11
+ root.render(
12
+ <React.StrictMode>
13
+ <App />
14
+ </React.StrictMode>
15
+ );
metadata.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "name": "Orchestrator-4",
3
+ "description": "A four-agent orchestration interface managing Jules (Coding), Developer (Planning), and General Agents, featuring intelligent delegation and deployment monitoring.",
4
+ "requestFramePermissions": []
5
+ }
multi_agent_system/jules_agent_client/agent.py CHANGED
@@ -124,7 +124,7 @@ if __name__ == '__main__':
124
  # Use the first available source for the example
125
  first_source = sources['sources'][0]['name']
126
  print(f"\n--- Creating Session on source: {first_source} ---")
127
-
128
  example_prompt = "Please add a new function to the main library file that calculates the factorial of a number."
129
  session_info = create_session(jules_key, first_source, example_prompt, title="Factorial Function Task")
130
  print(json.dumps(session_info, indent=2))
 
124
  # Use the first available source for the example
125
  first_source = sources['sources'][0]['name']
126
  print(f"\n--- Creating Session on source: {first_source} ---")
127
+
128
  example_prompt = "Please add a new function to the main library file that calculates the factorial of a number."
129
  session_info = create_session(jules_key, first_source, example_prompt, title="Factorial Function Task")
130
  print(json.dumps(session_info, indent=2))
multi_agent_system/llm_client.py CHANGED
@@ -31,7 +31,7 @@ def get_llm_response_stream(messages, model_name):
31
  messages=messages,
32
  stream=True,
33
  )
34
-
35
  for chunk in stream:
36
  content = chunk.choices[0].delta.content
37
  if content:
@@ -43,21 +43,21 @@ def get_llm_response_stream(messages, model_name):
43
  if __name__ == '__main__':
44
  # Example of how to use the LLM client stream
45
  # This requires the BLABLADOR_API_KEY to be set as an environment variable.
46
-
47
  print("--- Testing LLM Client Stream ---")
48
-
49
  example_messages = [
50
  {"role": "system", "content": "You are a helpful assistant."},
51
  {"role": "user", "content": "Tell me a short story about a robot who discovers music."}
52
  ]
53
-
54
  full_story = ""
55
  response_stream = get_llm_response_stream(messages=example_messages, model_name="alias-large")
56
-
57
  print("Streaming response:")
58
  for chunk in response_stream:
59
  print(chunk, end="", flush=True)
60
  full_story += chunk
61
-
62
  print("\n\n--- End of Stream ---")
63
  print(f"Final assembled story length: {len(full_story)} characters.")
 
31
  messages=messages,
32
  stream=True,
33
  )
34
+
35
  for chunk in stream:
36
  content = chunk.choices[0].delta.content
37
  if content:
 
43
  if __name__ == '__main__':
44
  # Example of how to use the LLM client stream
45
  # This requires the BLABLADOR_API_KEY to be set as an environment variable.
46
+
47
  print("--- Testing LLM Client Stream ---")
48
+
49
  example_messages = [
50
  {"role": "system", "content": "You are a helpful assistant."},
51
  {"role": "user", "content": "Tell me a short story about a robot who discovers music."}
52
  ]
53
+
54
  full_story = ""
55
  response_stream = get_llm_response_stream(messages=example_messages, model_name="alias-large")
56
+
57
  print("Streaming response:")
58
  for chunk in response_stream:
59
  print(chunk, end="", flush=True)
60
  full_story += chunk
61
+
62
  print("\n\n--- End of Stream ---")
63
  print(f"Final assembled story length: {len(full_story)} characters.")
multi_agent_system/monitor_agent/agent.py CHANGED
@@ -1,110 +1,106 @@
1
  import requests
2
  import os
3
  import time
4
- import sys
5
- import json
6
-
7
- # Add sibling directories to the Python path
8
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
9
 
10
- from jules_agent_client.agent import list_session_activities, send_message_to_session
11
- from llm_client import get_llm_response_stream
12
-
13
- # Constants
14
  HF_API_ENDPOINT = "https://huggingface.co"
15
- MONITOR_MODEL = "alias-fast"
16
 
17
  def get_space_logs(repo_id, target='container', token=None):
18
- """Retrieves logs from a Hugging Face Space repository."""
19
- # (Existing implementation remains the same)
 
 
 
 
 
 
 
 
 
20
  if not token:
21
  token = os.environ.get("HF_TOKEN")
 
22
  url = f'{HF_API_ENDPOINT}/api/spaces/{repo_id}/logs/{target}'
23
  headers = {'User-Agent': 'Jules-Monitor-Agent/1.0'}
24
  if token:
25
  headers['Authorization'] = f'Bearer {token}'
 
26
  try:
27
  response = requests.get(url, headers=headers, timeout=15)
28
- response.raise_for_status()
29
- return response.text
 
 
 
 
 
 
 
 
30
  except requests.exceptions.RequestException as e:
31
- return f'Error fetching logs: {str(e)}'
32
 
33
- def analyze_logs_with_llm(logs):
34
- """
35
- Uses an LLM to analyze logs for errors and returns a structured response.
36
- """
37
- if "Error fetching logs" in logs:
38
- return {"error_detected": True, "summary": logs}
39
-
40
- if not logs or logs.strip() == "":
41
- return {"error_detected": False, "summary": "Logs are empty."}
42
-
43
- messages = [
44
- {"role": "system", "content": "You are an expert log analysis agent. Your task is to determine if the provided Hugging Face space logs indicate a build or runtime error. Respond with a JSON object containing two keys: 'error_detected' (boolean) and 'summary' (a concise, one-sentence summary for another AI agent)."},
45
- {"role": "user", "content": f"Please analyze the following logs:\n\n---\n{logs}\n---"}
46
- ]
47
-
48
- full_response = ""
49
- for chunk in get_llm_response_stream(messages, MONITOR_MODEL):
50
- full_response += chunk
51
-
52
- try:
53
- # The response should be a JSON object string
54
- return json.loads(full_response)
55
- except json.JSONDecodeError:
56
- return {"error_detected": True, "summary": f"Log analysis failed. Raw LLM response: {full_response}"}
57
 
 
 
 
 
 
 
 
 
58
 
59
  def monitor_session(session_id, hf_repo_id, jules_api_key, hf_token=None):
60
  """
61
- Main monitoring loop that uses an LLM to intelligently analyze logs.
 
 
 
 
 
 
62
  """
63
- print(f"Starting intelligent monitoring for Jules session: {session_id} and HF Space: {hf_repo_id}")
 
64
 
65
  while True:
66
- # 1. Check Hugging Face logs and analyze them
67
  print(f"Checking HF logs for {hf_repo_id}...")
68
  container_logs = get_space_logs(hf_repo_id, target='container', token=hf_token)
69
-
70
- # Simple check for "running" status to gracefully exit
71
- if "running" in container_logs.lower():
72
- print("Application is running successfully. Stopping monitoring.")
73
- break
74
 
75
- print("Analyzing logs with LLM...")
76
- analysis = analyze_logs_with_llm(container_logs)
77
-
78
- if analysis.get("error_detected"):
79
- print("LLM detected an error in the logs. Reporting to Jules...")
80
- error_message = f"Monitoring agent detected a potential error. AI-generated summary:\n\n> {analysis.get('summary', 'No summary available.')}"
81
  send_message_to_session(jules_api_key, session_id, error_message)
 
82
  print("Error reported. Stopping monitoring for this session.")
83
  break
84
 
85
- print("No errors detected by LLM. Waiting for 10 minutes...")
86
- time.sleep(600)
 
 
87
 
88
- if __name__ == '__main__':
89
- # This example requires JULES_API_KEY, HF_TOKEN, and BLABLADOR_API_KEY to be set
90
- print("--- Testing Intelligent Monitor ---")
91
-
92
- # Simulate a log file with an error
93
- mock_error_logs = """
94
- File "/code/app.py", line 10, in <module>
95
- import non_existent_library
96
- ModuleNotFoundError: No module named 'non_existent_library'
97
- """
98
-
99
- print("--- Analyzing Mock Error Logs ---")
100
- error_analysis = analyze_logs_with_llm(mock_error_logs)
101
- print(f"Analysis Result: {error_analysis}")
102
- assert error_analysis.get("error_detected") is True
103
-
104
- print("\n--- Analyzing Mock Success Logs ---")
105
- mock_success_logs = "INFO: Uvicorn running on http://0.0.0.0:7860 (Press CTRL+C to quit)"
106
- success_analysis = analyze_logs_with_llm(mock_success_logs)
107
- print(f"Analysis Result: {success_analysis}")
108
- assert success_analysis.get("error_detected") is False
109
-
110
- print("\nIntelligent monitor tests passed.")
 
1
  import requests
2
  import os
3
  import time
 
 
 
 
 
4
 
 
 
 
 
5
  HF_API_ENDPOINT = "https://huggingface.co"
 
6
 
7
  def get_space_logs(repo_id, target='container', token=None):
8
+ """
9
+ Retrieves logs from a Hugging Face Space repository.
10
+
11
+ Args:
12
+ repo_id (str): The identifier of the repository (e.g., 'harvesthealth/magneticui').
13
+ target (str): The type of logs to retrieve ('build' or 'container').
14
+ token (str, optional): Hugging Face API token for authentication. Defaults to None.
15
+
16
+ Returns:
17
+ str: The log content as a string, or an error message.
18
+ """
19
  if not token:
20
  token = os.environ.get("HF_TOKEN")
21
+
22
  url = f'{HF_API_ENDPOINT}/api/spaces/{repo_id}/logs/{target}'
23
  headers = {'User-Agent': 'Jules-Monitor-Agent/1.0'}
24
  if token:
25
  headers['Authorization'] = f'Bearer {token}'
26
+
27
  try:
28
  response = requests.get(url, headers=headers, timeout=15)
29
+ if response.status_code == 200:
30
+ return response.text
31
+ elif response.status_code == 404:
32
+ return f'Error: Repository or logs not found (404). URL: {url}'
33
+ elif response.status_code == 401:
34
+ return f'Error: Unauthorized access. Token may be invalid or insufficient permissions (401).'
35
+ elif response.status_code == 403:
36
+ return f'Error: Access forbidden. Authentication may be required (403).'
37
+ else:
38
+ return f'Error: {response.status_code} - {response.text}'
39
  except requests.exceptions.RequestException as e:
40
+ return f'Exception during request: {str(e)}'
41
 
42
+ if __name__ == '__main__':
43
+ # This is an example of how to use the function.
44
+ # Replace with a real repo_id and ensure HF_TOKEN is set in your environment.
45
+ repo_id_to_test = "gradio/hello_world"
46
+ print("--- Retrieving Build Logs ---")
47
+ build_logs = get_space_logs(repo_id_to_test, target='build')
48
+ print(build_logs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
 
50
+ print("\n--- Retrieving Container Logs ---")
51
+ container_logs = get_space_logs(repo_id_to_test, target='container')
52
+ print(container_logs)
53
+
54
+ # Add sibling directories to the Python path to allow for intra-package imports
55
+ import sys
56
+ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
57
+ from jules_agent_client.agent import list_session_activities, send_message_to_session
58
 
59
  def monitor_session(session_id, hf_repo_id, jules_api_key, hf_token=None):
60
  """
61
+ Main monitoring loop for a given Jules session and Hugging Face space.
62
+
63
+ Args:
64
+ session_id (str): The Jules session ID to monitor.
65
+ hf_repo_id (str): The Hugging Face repo ID to monitor logs for.
66
+ jules_api_key (str): The Jules API key.
67
+ hf_token (str, optional): The Hugging Face API token. Defaults to None.
68
  """
69
+ print(f"Starting monitoring for Jules session: {session_id} and HF Space: {hf_repo_id}")
70
+ last_activity_count = 0
71
 
72
  while True:
73
+ # 1. Check Hugging Face logs for errors
74
  print(f"Checking HF logs for {hf_repo_id}...")
75
  container_logs = get_space_logs(hf_repo_id, target='container', token=hf_token)
 
 
 
 
 
76
 
77
+ # Simple error check: look for "error" string in logs
78
+ if "error" in container_logs.lower():
79
+ print("Error detected in HF container logs. Reporting to Jules...")
80
+ error_message = f"Monitoring agent detected an error in the Hugging Face container logs:\n\n---\n{container_logs}\n---"
 
 
81
  send_message_to_session(jules_api_key, session_id, error_message)
82
+ # Potentially stop monitoring after reporting an error, or wait for a fix
83
  print("Error reported. Stopping monitoring for this session.")
84
  break
85
 
86
+ # Check if the app is running
87
+ if "running" in container_logs.lower(): # This is a simplistic check
88
+ print("Application is running successfully. Stopping monitoring.")
89
+ break
90
 
91
+ # 2. Check for new Jules activities
92
+ print(f"Checking Jules activities for session {session_id}...")
93
+ activities_response = list_session_activities(jules_api_key, session_id)
94
+
95
+ if 'activities' in activities_response:
96
+ current_activity_count = len(activities_response['activities'])
97
+ if current_activity_count > last_activity_count:
98
+ print(f"New activities detected ({current_activity_count - last_activity_count}).")
99
+ # You could add logic here to parse new activities
100
+ last_activity_count = current_activity_count
101
+ else:
102
+ print("No new Jules activities.")
103
+
104
+ # Wait for 10 minutes before the next check
105
+ print("Waiting for 10 minutes...")
106
+ time.sleep(600)
 
 
 
 
 
 
 
multi_agent_system/orchestrator/agent.py CHANGED
@@ -38,7 +38,7 @@ def generate_conversational_update(action_description):
38
  {"role": "system", "content": "You are an orchestrator agent. Your role is to provide clear, friendly, and slightly enthusiastic updates to the user about your progress. Keep your responses concise (1-2 sentences)."},
39
  {"role": "user", "content": f"Please provide a conversational update for the following action: {action_description}"}
40
  ]
41
-
42
  full_response = ""
43
  for chunk in get_llm_response_stream(messages, ORCHESTRATOR_MODEL):
44
  full_response += chunk
@@ -62,7 +62,7 @@ def main_orchestration_workflow():
62
 
63
  # Step 1: Get plan from the developer agent
64
  yield generate_conversational_update("Contacting the specialist Developer Agent for a project plan.")
65
-
66
  developer_prompt = """
67
  Your task is to act as a planner for a new software development project.
68
  You must investigate the repository at https://github.com/JsonLord/desk_agent.git,
@@ -72,7 +72,7 @@ def main_orchestration_workflow():
72
  Your deliverables are a single JSON object with four keys: "idea", "tasks", "context_files", and "hf_repo".
73
  """
74
  plan_response = send_prompt_to_developer_agent(developer_prompt)
75
-
76
  try:
77
  plan_data = json.loads(plan_response)
78
  if "error" in plan_data:
@@ -88,7 +88,7 @@ def main_orchestration_workflow():
88
  if not sources or 'sources' not in sources or not sources['sources']:
89
  yield "I couldn't find any available source repositories for Jules."
90
  return None
91
-
92
  target_source_name = sources['sources'][0]['name']
93
  repo_name = target_source_name.split('/')[-1]
94
 
@@ -111,7 +111,7 @@ def main_orchestration_workflow():
111
  if 'error' in session_info or 'name' not in session_info:
112
  yield f"Something went wrong when I tried to start the Jules session. Details: {session_info}"
113
  return None
114
-
115
  session_id = session_info['name'].split('/')[-1]
116
  yield generate_conversational_update(f"Success! The coding session with Jules is active (ID: {session_id}).")
117
 
@@ -122,7 +122,7 @@ def main_orchestration_workflow():
122
  return None
123
 
124
  yield generate_conversational_update(f"I'm now handing off to the Monitor Agent to watch the deployment at '{hf_repo_to_monitor}'.")
125
-
126
  # Return the necessary data for the monitor to be started in a separate thread
127
  return {
128
  "session_id": session_id,
 
38
  {"role": "system", "content": "You are an orchestrator agent. Your role is to provide clear, friendly, and slightly enthusiastic updates to the user about your progress. Keep your responses concise (1-2 sentences)."},
39
  {"role": "user", "content": f"Please provide a conversational update for the following action: {action_description}"}
40
  ]
41
+
42
  full_response = ""
43
  for chunk in get_llm_response_stream(messages, ORCHESTRATOR_MODEL):
44
  full_response += chunk
 
62
 
63
  # Step 1: Get plan from the developer agent
64
  yield generate_conversational_update("Contacting the specialist Developer Agent for a project plan.")
65
+
66
  developer_prompt = """
67
  Your task is to act as a planner for a new software development project.
68
  You must investigate the repository at https://github.com/JsonLord/desk_agent.git,
 
72
  Your deliverables are a single JSON object with four keys: "idea", "tasks", "context_files", and "hf_repo".
73
  """
74
  plan_response = send_prompt_to_developer_agent(developer_prompt)
75
+
76
  try:
77
  plan_data = json.loads(plan_response)
78
  if "error" in plan_data:
 
88
  if not sources or 'sources' not in sources or not sources['sources']:
89
  yield "I couldn't find any available source repositories for Jules."
90
  return None
91
+
92
  target_source_name = sources['sources'][0]['name']
93
  repo_name = target_source_name.split('/')[-1]
94
 
 
111
  if 'error' in session_info or 'name' not in session_info:
112
  yield f"Something went wrong when I tried to start the Jules session. Details: {session_info}"
113
  return None
114
+
115
  session_id = session_info['name'].split('/')[-1]
116
  yield generate_conversational_update(f"Success! The coding session with Jules is active (ID: {session_id}).")
117
 
 
122
  return None
123
 
124
  yield generate_conversational_update(f"I'm now handing off to the Monitor Agent to watch the deployment at '{hf_repo_to_monitor}'.")
125
+
126
  # Return the necessary data for the monitor to be started in a separate thread
127
  return {
128
  "session_id": session_id,
package.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "orchestrator-4",
3
+ "private": true,
4
+ "version": "0.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "react": "^19.2.3",
13
+ "react-dom": "^19.2.3",
14
+ "lucide-react": "^0.562.0"
15
+ },
16
+ "devDependencies": {
17
+ "@types/node": "^22.14.0",
18
+ "@vitejs/plugin-react": "^5.0.0",
19
+ "typescript": "~5.8.2",
20
+ "vite": "^6.2.0"
21
+ }
22
+ }
requirements.txt CHANGED
@@ -1,4 +1,4 @@
1
  gradio
2
  huggingface-hub
3
  requests
4
- openai
 
1
  gradio
2
  huggingface-hub
3
  requests
4
+ openai
services/julesService.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { JULES_API_BASE, JULES_API_KEY } from '../constants';
2
+ import { JulesSession } from '../types';
3
+
4
+ // Helper to simulate API delay
5
+ const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
6
+
7
+ export const createJulesSession = async (title: string, prompt: string, repo: string): Promise<JulesSession> => {
8
+ // In a real app, this would be a fetch call.
9
+ // Due to potential CORS on the provided endpoint from a browser, we mock the success logic here
10
+ // based on the curl command provided in the prompt.
11
+
12
+ /*
13
+ REAL IMPLEMENTATION:
14
+ const response = await fetch(`${JULES_API_BASE}/sessions`, {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ 'X-Goog-Api-Key': JULES_API_KEY
19
+ },
20
+ body: JSON.stringify({
21
+ prompt: prompt,
22
+ sourceContext: {
23
+ source: `sources/github/${repo}`,
24
+ githubRepoContext: { startingBranch: "main" }
25
+ },
26
+ automationMode: "AUTO_CREATE_PR",
27
+ title: title
28
+ })
29
+ });
30
+ return response.json();
31
+ */
32
+
33
+ console.log(`[JulesAPI] Creating session for repo: ${repo}`);
34
+ await delay(1500);
35
+
36
+ return {
37
+ sessionId: `sess_${Math.random().toString(36).substring(7)}`,
38
+ title: title,
39
+ status: 'active',
40
+ lastLog: 'Session initialized. Analyzing repository...'
41
+ };
42
+ };
43
+
44
+ export const sendMessageToJules = async (sessionId: string, message: string) => {
45
+ console.log(`[JulesAPI] Sending message to ${sessionId}: ${message}`);
46
+ await delay(1000);
47
+ return {
48
+ response: "I have received your request. I am checking the codebase context now."
49
+ };
50
+ };
51
+
52
+ export const listJulesActivities = async (sessionId: string) => {
53
+ console.log(`[JulesAPI] Listing activities for ${sessionId}`);
54
+ await delay(800);
55
+ return [
56
+ { type: 'activity', status: 'completed', description: 'Repo clone successful' },
57
+ { type: 'activity', status: 'in_progress', description: 'Analyzing /deployment directory' }
58
+ ];
59
+ };
tsconfig.json ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "experimentalDecorators": true,
5
+ "useDefineForClassFields": false,
6
+ "module": "ESNext",
7
+ "lib": [
8
+ "ES2022",
9
+ "DOM",
10
+ "DOM.Iterable"
11
+ ],
12
+ "skipLibCheck": true,
13
+ "types": [
14
+ "node"
15
+ ],
16
+ "moduleResolution": "bundler",
17
+ "isolatedModules": true,
18
+ "moduleDetection": "force",
19
+ "allowJs": true,
20
+ "jsx": "react-jsx",
21
+ "paths": {
22
+ "@/*": [
23
+ "./*"
24
+ ]
25
+ },
26
+ "allowImportingTsExtensions": true,
27
+ "noEmit": true
28
+ }
29
+ }
types.ts ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ export enum AgentType {
2
+ ORCHESTRATOR = 'Orchestrator',
3
+ JULES = 'Jules (Coder)',
4
+ DEVELOPER = 'Developer (Planner)',
5
+ GENERAL = 'General Agent',
6
+ MONITOR = 'Monitoring Agent'
7
+ }
8
+
9
+ export interface Message {
10
+ id: string;
11
+ sender: AgentType | 'User';
12
+ content: string;
13
+ timestamp: Date;
14
+ metadata?: any; // For logs, session IDs, etc.
15
+ }
16
+
17
+ export interface AgentConfig {
18
+ name: string;
19
+ type: AgentType;
20
+ endpoint?: string;
21
+ description: string;
22
+ status: 'idle' | 'working' | 'error';
23
+ }
24
+
25
+ export interface JulesSession {
26
+ sessionId: string;
27
+ title: string;
28
+ status: 'active' | 'completed' | 'failed';
29
+ lastLog: string;
30
+ }
31
+
32
+ export interface MonitoringLog {
33
+ id: string;
34
+ timestamp: string;
35
+ source: 'JulesAPI' | 'HuggingFace' | 'System';
36
+ level: 'INFO' | 'WARN' | 'ERROR';
37
+ message: string;
38
+ }
vite.config.ts ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import path from 'path';
2
+ import { defineConfig, loadEnv } from 'vite';
3
+ import react from '@vitejs/plugin-react';
4
+
5
+ export default defineConfig(({ mode }) => {
6
+ const env = loadEnv(mode, '.', '');
7
+ return {
8
+ server: {
9
+ port: 3000,
10
+ host: '0.0.0.0',
11
+ },
12
+ plugins: [react()],
13
+ define: {
14
+ 'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
15
+ 'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
16
+ },
17
+ resolve: {
18
+ alias: {
19
+ '@': path.resolve(__dirname, '.'),
20
+ }
21
+ }
22
+ };
23
+ });