agent-framework-devui 1.0.0b260106__py3-none-any.whl
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.
- agent_framework_devui/__init__.py +277 -0
- agent_framework_devui/_cli.py +201 -0
- agent_framework_devui/_conversations.py +631 -0
- agent_framework_devui/_deployment.py +596 -0
- agent_framework_devui/_discovery.py +944 -0
- agent_framework_devui/_executor.py +1015 -0
- agent_framework_devui/_mapper.py +1962 -0
- agent_framework_devui/_openai/__init__.py +9 -0
- agent_framework_devui/_openai/_executor.py +270 -0
- agent_framework_devui/_server.py +1306 -0
- agent_framework_devui/_session.py +191 -0
- agent_framework_devui/_tracing.py +168 -0
- agent_framework_devui/_utils.py +635 -0
- agent_framework_devui/models/__init__.py +94 -0
- agent_framework_devui/models/_discovery_models.py +201 -0
- agent_framework_devui/models/_openai_custom.py +411 -0
- agent_framework_devui/ui/agentframework.svg +33 -0
- agent_framework_devui/ui/assets/index.css +1 -0
- agent_framework_devui/ui/assets/index.js +802 -0
- agent_framework_devui/ui/index.html +14 -0
- agent_framework_devui-1.0.0b260106.dist-info/METADATA +393 -0
- agent_framework_devui-1.0.0b260106.dist-info/RECORD +25 -0
- agent_framework_devui-1.0.0b260106.dist-info/WHEEL +4 -0
- agent_framework_devui-1.0.0b260106.dist-info/entry_points.txt +3 -0
- agent_framework_devui-1.0.0b260106.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Agent Framework DevUI - Debug interface with OpenAI compatible API server."""
|
|
4
|
+
|
|
5
|
+
import importlib.metadata
|
|
6
|
+
import logging
|
|
7
|
+
import webbrowser
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
from ._conversations import CheckpointConversationManager
|
|
12
|
+
from ._server import DevServer
|
|
13
|
+
from .models import AgentFrameworkRequest, OpenAIError, OpenAIResponse, ResponseStreamEvent
|
|
14
|
+
from .models._discovery_models import DiscoveryResponse, EntityInfo, EnvVarRequirement
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
# Module-level cleanup registry (before serve() is called)
|
|
19
|
+
_cleanup_registry: dict[int, list[Callable[[], Any]]] = {}
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
__version__ = importlib.metadata.version(__name__)
|
|
23
|
+
except importlib.metadata.PackageNotFoundError:
|
|
24
|
+
__version__ = "0.0.0" # Fallback for development mode
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def register_cleanup(entity: Any, *hooks: Callable[[], Any]) -> None:
|
|
28
|
+
"""Register cleanup hook(s) for an entity.
|
|
29
|
+
|
|
30
|
+
Cleanup hooks execute during DevUI server shutdown, before entity
|
|
31
|
+
clients are closed. Supports both synchronous and asynchronous callables.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
entity: Agent, workflow, or other entity object
|
|
35
|
+
*hooks: One or more cleanup callables (sync or async)
|
|
36
|
+
|
|
37
|
+
Raises:
|
|
38
|
+
ValueError: If no hooks provided
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
Single cleanup hook:
|
|
42
|
+
>>> from agent_framework.devui import serve, register_cleanup
|
|
43
|
+
>>> credential = DefaultAzureCredential()
|
|
44
|
+
>>> agent = ChatAgent(...)
|
|
45
|
+
>>> register_cleanup(agent, credential.close)
|
|
46
|
+
>>> serve(entities=[agent])
|
|
47
|
+
|
|
48
|
+
Multiple cleanup hooks:
|
|
49
|
+
>>> register_cleanup(agent, credential.close, session.close, db_pool.close)
|
|
50
|
+
|
|
51
|
+
Works with file-based discovery:
|
|
52
|
+
>>> # In agents/my_agent/agent.py
|
|
53
|
+
>>> from agent_framework.devui import register_cleanup
|
|
54
|
+
>>> credential = DefaultAzureCredential()
|
|
55
|
+
>>> agent = ChatAgent(...)
|
|
56
|
+
>>> register_cleanup(agent, credential.close)
|
|
57
|
+
>>> # Run: devui ./agents
|
|
58
|
+
"""
|
|
59
|
+
if not hooks:
|
|
60
|
+
raise ValueError("At least one cleanup hook required")
|
|
61
|
+
|
|
62
|
+
# Use id() to track entity identity (works across modules)
|
|
63
|
+
entity_id = id(entity)
|
|
64
|
+
|
|
65
|
+
if entity_id not in _cleanup_registry:
|
|
66
|
+
_cleanup_registry[entity_id] = []
|
|
67
|
+
|
|
68
|
+
_cleanup_registry[entity_id].extend(hooks)
|
|
69
|
+
|
|
70
|
+
logger.debug(
|
|
71
|
+
f"Registered {len(hooks)} cleanup hook(s) for {type(entity).__name__} "
|
|
72
|
+
f"(id: {entity_id}, total: {len(_cleanup_registry[entity_id])})"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_registered_cleanup_hooks(entity: Any) -> list[Callable[[], Any]]:
|
|
77
|
+
"""Get cleanup hooks registered for an entity (internal use).
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
entity: Entity object to get hooks for
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
List of cleanup hooks registered for the entity
|
|
84
|
+
"""
|
|
85
|
+
entity_id = id(entity)
|
|
86
|
+
return _cleanup_registry.get(entity_id, [])
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def serve(
|
|
90
|
+
entities: list[Any] | None = None,
|
|
91
|
+
entities_dir: str | None = None,
|
|
92
|
+
port: int = 8080,
|
|
93
|
+
host: str = "127.0.0.1",
|
|
94
|
+
auto_open: bool = False,
|
|
95
|
+
cors_origins: list[str] | None = None,
|
|
96
|
+
ui_enabled: bool = True,
|
|
97
|
+
tracing_enabled: bool = False,
|
|
98
|
+
mode: str = "developer",
|
|
99
|
+
auth_enabled: bool = False,
|
|
100
|
+
auth_token: str | None = None,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Launch Agent Framework DevUI with simple API.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
entities: List of entities for in-memory registration (IDs auto-generated)
|
|
106
|
+
entities_dir: Directory to scan for entities
|
|
107
|
+
port: Port to run server on
|
|
108
|
+
host: Host to bind server to
|
|
109
|
+
auto_open: Whether to automatically open browser
|
|
110
|
+
cors_origins: List of allowed CORS origins
|
|
111
|
+
ui_enabled: Whether to enable the UI
|
|
112
|
+
tracing_enabled: Whether to enable OpenTelemetry tracing
|
|
113
|
+
mode: Server mode - 'developer' (full access, verbose errors) or 'user' (restricted APIs, generic errors)
|
|
114
|
+
auth_enabled: Whether to enable Bearer token authentication
|
|
115
|
+
auth_token: Custom authentication token (auto-generated if not provided with auth_enabled=True)
|
|
116
|
+
"""
|
|
117
|
+
import re
|
|
118
|
+
|
|
119
|
+
import uvicorn
|
|
120
|
+
|
|
121
|
+
# Validate host parameter early for security
|
|
122
|
+
if not re.match(r"^(localhost|127\.0\.0\.1|0\.0\.0\.0|[a-zA-Z0-9.-]+)$", host):
|
|
123
|
+
raise ValueError(f"Invalid host: {host}. Must be localhost, IP address, or valid hostname")
|
|
124
|
+
|
|
125
|
+
# Validate port parameter
|
|
126
|
+
if not isinstance(port, int) or not (1 <= port <= 65535):
|
|
127
|
+
raise ValueError(f"Invalid port: {port}. Must be integer between 1 and 65535")
|
|
128
|
+
|
|
129
|
+
# Security check: Warn if network-exposed without authentication
|
|
130
|
+
if host not in ("127.0.0.1", "localhost") and not auth_enabled:
|
|
131
|
+
logger.warning("⚠️ WARNING: Exposing DevUI to network without authentication!")
|
|
132
|
+
logger.warning("⚠️ This is INSECURE - anyone on your network can access your agents")
|
|
133
|
+
logger.warning("💡 For network exposure, add --auth flag: devui --host 0.0.0.0 --auth")
|
|
134
|
+
|
|
135
|
+
# Handle authentication configuration
|
|
136
|
+
if auth_enabled:
|
|
137
|
+
import os
|
|
138
|
+
import secrets
|
|
139
|
+
|
|
140
|
+
# Check if token is in environment variable first
|
|
141
|
+
if not auth_token:
|
|
142
|
+
auth_token = os.environ.get("DEVUI_AUTH_TOKEN")
|
|
143
|
+
|
|
144
|
+
# Auto-generate token if STILL not provided
|
|
145
|
+
if not auth_token:
|
|
146
|
+
# Check if we're in a production-like environment
|
|
147
|
+
is_production = (
|
|
148
|
+
host not in ("127.0.0.1", "localhost") # Exposed to network
|
|
149
|
+
or os.environ.get("CI") == "true" # Running in CI
|
|
150
|
+
or os.environ.get("KUBERNETES_SERVICE_HOST") # Running in k8s
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if is_production:
|
|
154
|
+
# REFUSE to start without explicit token
|
|
155
|
+
logger.error("❌ Authentication enabled but no token provided")
|
|
156
|
+
logger.error("❌ Auto-generated tokens are NOT secure for network-exposed deployments")
|
|
157
|
+
logger.error("💡 Set token: export DEVUI_AUTH_TOKEN=<your-secure-token>")
|
|
158
|
+
logger.error("💡 Or pass: serve(entities=[...], auth_token='your-token')")
|
|
159
|
+
raise ValueError("DEVUI_AUTH_TOKEN required when host is not localhost")
|
|
160
|
+
|
|
161
|
+
# Development mode: auto-generate and show
|
|
162
|
+
auth_token = secrets.token_urlsafe(32)
|
|
163
|
+
logger.info("🔒 Authentication enabled with auto-generated token")
|
|
164
|
+
logger.info("\n" + "=" * 70)
|
|
165
|
+
logger.info("🔑 DEV TOKEN (localhost only, shown once):")
|
|
166
|
+
logger.info(f" {auth_token}")
|
|
167
|
+
logger.info("=" * 70 + "\n")
|
|
168
|
+
else:
|
|
169
|
+
logger.info("🔒 Authentication enabled with provided token")
|
|
170
|
+
|
|
171
|
+
# Set environment variable for server to use
|
|
172
|
+
os.environ["AUTH_REQUIRED"] = "true"
|
|
173
|
+
os.environ["DEVUI_AUTH_TOKEN"] = auth_token
|
|
174
|
+
|
|
175
|
+
# Configure tracing environment variables if enabled
|
|
176
|
+
if tracing_enabled:
|
|
177
|
+
import os
|
|
178
|
+
|
|
179
|
+
# Only set if not already configured by user
|
|
180
|
+
if not os.environ.get("ENABLE_INSTRUMENTATION"):
|
|
181
|
+
os.environ["ENABLE_INSTRUMENTATION"] = "true"
|
|
182
|
+
logger.info("Set ENABLE_INSTRUMENTATION=true for tracing")
|
|
183
|
+
|
|
184
|
+
if not os.environ.get("ENABLE_SENSITIVE_DATA"):
|
|
185
|
+
os.environ["ENABLE_SENSITIVE_DATA"] = "true"
|
|
186
|
+
logger.info("Set ENABLE_SENSITIVE_DATA=true for tracing")
|
|
187
|
+
|
|
188
|
+
if not os.environ.get("OTLP_ENDPOINT"):
|
|
189
|
+
os.environ["OTLP_ENDPOINT"] = "http://localhost:4317"
|
|
190
|
+
logger.info("Set OTLP_ENDPOINT=http://localhost:4317 for tracing")
|
|
191
|
+
|
|
192
|
+
# Create server with direct parameters
|
|
193
|
+
server = DevServer(
|
|
194
|
+
entities_dir=entities_dir,
|
|
195
|
+
port=port,
|
|
196
|
+
host=host,
|
|
197
|
+
cors_origins=cors_origins,
|
|
198
|
+
ui_enabled=ui_enabled,
|
|
199
|
+
mode=mode,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Register in-memory entities if provided
|
|
203
|
+
if entities:
|
|
204
|
+
logger.info(f"Registering {len(entities)} in-memory entities")
|
|
205
|
+
# Store entities for later registration during server startup
|
|
206
|
+
server._pending_entities = entities
|
|
207
|
+
|
|
208
|
+
app = server.get_app()
|
|
209
|
+
|
|
210
|
+
if auto_open:
|
|
211
|
+
|
|
212
|
+
def open_browser() -> None:
|
|
213
|
+
import http.client
|
|
214
|
+
import re
|
|
215
|
+
import time
|
|
216
|
+
|
|
217
|
+
# Validate host and port for security
|
|
218
|
+
if not re.match(r"^(localhost|127\.0\.0\.1|0\.0\.0\.0|[a-zA-Z0-9.-]+)$", host):
|
|
219
|
+
logger.warning(f"Invalid host for auto-open: {host}")
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
if not isinstance(port, int) or not (1 <= port <= 65535):
|
|
223
|
+
logger.warning(f"Invalid port for auto-open: {port}")
|
|
224
|
+
return
|
|
225
|
+
|
|
226
|
+
# Wait for server to be ready by checking health endpoint
|
|
227
|
+
browser_url = f"http://{host}:{port}"
|
|
228
|
+
|
|
229
|
+
for _ in range(30): # 15 second timeout (30 * 0.5s)
|
|
230
|
+
try:
|
|
231
|
+
# Use http.client for safe connection handling (standard library)
|
|
232
|
+
conn = http.client.HTTPConnection(host, port, timeout=1)
|
|
233
|
+
try:
|
|
234
|
+
conn.request("GET", "/health")
|
|
235
|
+
response = conn.getresponse()
|
|
236
|
+
if response.status == 200:
|
|
237
|
+
webbrowser.open(browser_url)
|
|
238
|
+
return
|
|
239
|
+
finally:
|
|
240
|
+
conn.close()
|
|
241
|
+
except (http.client.HTTPException, OSError, TimeoutError):
|
|
242
|
+
pass
|
|
243
|
+
time.sleep(0.5)
|
|
244
|
+
|
|
245
|
+
# Fallback: open browser anyway after timeout
|
|
246
|
+
webbrowser.open(browser_url)
|
|
247
|
+
|
|
248
|
+
import threading
|
|
249
|
+
|
|
250
|
+
threading.Thread(target=open_browser, daemon=True).start()
|
|
251
|
+
|
|
252
|
+
logger.info(f"Starting Agent Framework DevUI on {host}:{port}")
|
|
253
|
+
uvicorn.run(app, host=host, port=port, log_level="info")
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def main() -> None:
|
|
257
|
+
"""CLI entry point for devui command."""
|
|
258
|
+
from ._cli import main as cli_main
|
|
259
|
+
|
|
260
|
+
cli_main()
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
# Export main public API
|
|
264
|
+
__all__ = [
|
|
265
|
+
"AgentFrameworkRequest",
|
|
266
|
+
"CheckpointConversationManager",
|
|
267
|
+
"DevServer",
|
|
268
|
+
"DiscoveryResponse",
|
|
269
|
+
"EntityInfo",
|
|
270
|
+
"EnvVarRequirement",
|
|
271
|
+
"OpenAIError",
|
|
272
|
+
"OpenAIResponse",
|
|
273
|
+
"ResponseStreamEvent",
|
|
274
|
+
"main",
|
|
275
|
+
"register_cleanup",
|
|
276
|
+
"serve",
|
|
277
|
+
]
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# Copyright (c) Microsoft. All rights reserved.
|
|
2
|
+
|
|
3
|
+
"""Command line interface for Agent Framework DevUI."""
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def setup_logging(level: str = "INFO") -> None:
|
|
14
|
+
"""Configure logging for the server."""
|
|
15
|
+
log_format = "%(asctime)s [%(levelname)s] %(name)s: %(message)s"
|
|
16
|
+
logging.basicConfig(level=getattr(logging, level.upper()), format=log_format, datefmt="%Y-%m-%d %H:%M:%S")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def create_cli_parser() -> argparse.ArgumentParser:
|
|
20
|
+
"""Create the command line argument parser."""
|
|
21
|
+
parser = argparse.ArgumentParser(
|
|
22
|
+
prog="devui",
|
|
23
|
+
description="Launch Agent Framework DevUI - Debug interface with OpenAI compatible API",
|
|
24
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
25
|
+
epilog="""
|
|
26
|
+
Examples:
|
|
27
|
+
devui # Scan current directory
|
|
28
|
+
devui ./agents # Scan specific directory
|
|
29
|
+
devui --port 8000 # Custom port
|
|
30
|
+
devui --headless # API only, no UI
|
|
31
|
+
devui --tracing # Enable OpenTelemetry tracing
|
|
32
|
+
""",
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
parser.add_argument(
|
|
36
|
+
"directory", nargs="?", default=".", help="Directory to scan for entities (default: current directory)"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
parser.add_argument("--port", "-p", type=int, default=8080, help="Port to run server on (default: 8080)")
|
|
40
|
+
|
|
41
|
+
parser.add_argument("--host", default="127.0.0.1", help="Host to bind server to (default: 127.0.0.1)")
|
|
42
|
+
|
|
43
|
+
parser.add_argument("--no-open", action="store_true", help="Don't automatically open browser")
|
|
44
|
+
|
|
45
|
+
parser.add_argument("--headless", action="store_true", help="Run without UI (API only)")
|
|
46
|
+
|
|
47
|
+
parser.add_argument(
|
|
48
|
+
"--log-level",
|
|
49
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR"],
|
|
50
|
+
default="INFO",
|
|
51
|
+
help="Logging level (default: INFO)",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
parser.add_argument("--reload", action="store_true", help="Enable auto-reload for development")
|
|
55
|
+
|
|
56
|
+
parser.add_argument("--tracing", action="store_true", help="Enable OpenTelemetry tracing for Agent Framework")
|
|
57
|
+
|
|
58
|
+
parser.add_argument(
|
|
59
|
+
"--mode",
|
|
60
|
+
choices=["developer", "user"],
|
|
61
|
+
default=None,
|
|
62
|
+
help="Server mode - 'developer' (full access, verbose errors) or 'user' (restricted APIs, generic errors)",
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Add --dev/--no-dev as a convenient alternative to --mode
|
|
66
|
+
parser.add_argument(
|
|
67
|
+
"--dev",
|
|
68
|
+
dest="dev_mode",
|
|
69
|
+
action="store_true",
|
|
70
|
+
default=None,
|
|
71
|
+
help="Enable developer mode (shorthand for --mode developer)",
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
parser.add_argument(
|
|
75
|
+
"--no-dev",
|
|
76
|
+
dest="dev_mode",
|
|
77
|
+
action="store_false",
|
|
78
|
+
help="Disable developer mode (shorthand for --mode user)",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
parser.add_argument(
|
|
82
|
+
"--auth",
|
|
83
|
+
action="store_true",
|
|
84
|
+
help="Enable authentication via Bearer token (required for deployed environments)",
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"--auth-token",
|
|
89
|
+
type=str,
|
|
90
|
+
help="Custom authentication token (auto-generated if not provided with --auth)",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
parser.add_argument("--version", action="version", version=f"Agent Framework DevUI {get_version()}")
|
|
94
|
+
|
|
95
|
+
return parser
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def get_version() -> str:
|
|
99
|
+
"""Get the package version."""
|
|
100
|
+
try:
|
|
101
|
+
from . import __version__
|
|
102
|
+
|
|
103
|
+
return __version__
|
|
104
|
+
except ImportError:
|
|
105
|
+
return "unknown"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def validate_directory(directory: str) -> str:
|
|
109
|
+
"""Validate and normalize the entities directory."""
|
|
110
|
+
if not directory:
|
|
111
|
+
directory = "."
|
|
112
|
+
|
|
113
|
+
abs_dir = os.path.abspath(directory)
|
|
114
|
+
|
|
115
|
+
if not os.path.exists(abs_dir):
|
|
116
|
+
print(f"Error: Directory '{directory}' does not exist", file=sys.stderr) # noqa: T201
|
|
117
|
+
sys.exit(1)
|
|
118
|
+
|
|
119
|
+
if not os.path.isdir(abs_dir):
|
|
120
|
+
print(f"Error: '{directory}' is not a directory", file=sys.stderr) # noqa: T201
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
|
|
123
|
+
return abs_dir
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def print_startup_info(
|
|
127
|
+
entities_dir: str, host: str, port: int, ui_enabled: bool, reload: bool, auth_token: str | None = None
|
|
128
|
+
) -> None:
|
|
129
|
+
"""Print startup information."""
|
|
130
|
+
print("Agent Framework DevUI") # noqa: T201
|
|
131
|
+
print("=" * 50) # noqa: T201
|
|
132
|
+
print(f"Entities directory: {entities_dir}") # noqa: T201
|
|
133
|
+
print(f"Server URL: http://{host}:{port}") # noqa: T201
|
|
134
|
+
print(f"UI enabled: {'Yes' if ui_enabled else 'No'}") # noqa: T201
|
|
135
|
+
print(f"Auto-reload: {'Yes' if reload else 'No'}") # noqa: T201
|
|
136
|
+
|
|
137
|
+
# Display auth token if authentication is enabled
|
|
138
|
+
if auth_token:
|
|
139
|
+
print("Authentication: Enabled") # noqa: T201
|
|
140
|
+
print(f"Auth token: {auth_token}") # noqa: T201
|
|
141
|
+
print("💡 Use this token in Authorization: Bearer <token> header") # noqa: T201
|
|
142
|
+
|
|
143
|
+
print("=" * 50) # noqa: T201
|
|
144
|
+
print("Scanning for entities...") # noqa: T201
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def main() -> None:
|
|
148
|
+
"""Main CLI entry point."""
|
|
149
|
+
parser = create_cli_parser()
|
|
150
|
+
args = parser.parse_args()
|
|
151
|
+
|
|
152
|
+
# Setup logging
|
|
153
|
+
setup_logging(args.log_level)
|
|
154
|
+
|
|
155
|
+
# Validate directory
|
|
156
|
+
entities_dir = validate_directory(args.directory)
|
|
157
|
+
|
|
158
|
+
# Extract parameters directly from args
|
|
159
|
+
ui_enabled = not args.headless
|
|
160
|
+
|
|
161
|
+
# Determine mode from --mode or --dev/--no-dev flags
|
|
162
|
+
if args.dev_mode is not None:
|
|
163
|
+
# --dev or --no-dev was specified
|
|
164
|
+
mode = "developer" if args.dev_mode else "user"
|
|
165
|
+
elif args.mode is not None:
|
|
166
|
+
# --mode was specified
|
|
167
|
+
mode = args.mode
|
|
168
|
+
else:
|
|
169
|
+
# Default to developer mode
|
|
170
|
+
mode = "developer"
|
|
171
|
+
|
|
172
|
+
# Print startup info (don't show token - serve() will handle it)
|
|
173
|
+
print_startup_info(entities_dir, args.host, args.port, ui_enabled, args.reload, None)
|
|
174
|
+
|
|
175
|
+
# Import and start server
|
|
176
|
+
try:
|
|
177
|
+
from . import serve
|
|
178
|
+
|
|
179
|
+
serve(
|
|
180
|
+
entities_dir=entities_dir,
|
|
181
|
+
port=args.port,
|
|
182
|
+
host=args.host,
|
|
183
|
+
auto_open=not args.no_open,
|
|
184
|
+
ui_enabled=ui_enabled,
|
|
185
|
+
tracing_enabled=args.tracing,
|
|
186
|
+
mode=mode,
|
|
187
|
+
auth_enabled=args.auth,
|
|
188
|
+
auth_token=args.auth_token, # Pass through explicit token only
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
except KeyboardInterrupt:
|
|
192
|
+
print("\nShutting down Agent Framework DevUI...") # noqa: T201
|
|
193
|
+
sys.exit(0)
|
|
194
|
+
except Exception as e:
|
|
195
|
+
logger.exception("Failed to start server")
|
|
196
|
+
print(f"Error: {e}", file=sys.stderr) # noqa: T201
|
|
197
|
+
sys.exit(1)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
if __name__ == "__main__":
|
|
201
|
+
main()
|