agent-framework-devui 0.0.1a0__py3-none-any.whl → 1.0.0b251001__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.

Potentially problematic release.


This version of agent-framework-devui might be problematic. Click here for more details.

@@ -0,0 +1,151 @@
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 typing import Any
9
+
10
+ from ._server import DevServer
11
+ from .models import AgentFrameworkRequest, OpenAIError, OpenAIResponse, ResponseStreamEvent
12
+ from .models._discovery_models import DiscoveryResponse, EntityInfo, EnvVarRequirement
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ try:
17
+ __version__ = importlib.metadata.version(__name__)
18
+ except importlib.metadata.PackageNotFoundError:
19
+ __version__ = "0.0.0" # Fallback for development mode
20
+
21
+
22
+ def serve(
23
+ entities: list[Any] | None = None,
24
+ entities_dir: str | None = None,
25
+ port: int = 8080,
26
+ host: str = "127.0.0.1",
27
+ auto_open: bool = False,
28
+ cors_origins: list[str] | None = None,
29
+ ui_enabled: bool = True,
30
+ tracing_enabled: bool = False,
31
+ ) -> None:
32
+ """Launch Agent Framework DevUI with simple API.
33
+
34
+ Args:
35
+ entities: List of entities for in-memory registration (IDs auto-generated)
36
+ entities_dir: Directory to scan for entities
37
+ port: Port to run server on
38
+ host: Host to bind server to
39
+ auto_open: Whether to automatically open browser
40
+ cors_origins: List of allowed CORS origins
41
+ ui_enabled: Whether to enable the UI
42
+ tracing_enabled: Whether to enable OpenTelemetry tracing
43
+ """
44
+ import re
45
+
46
+ import uvicorn
47
+
48
+ # Validate host parameter early for security
49
+ if not re.match(r"^(localhost|127\.0\.0\.1|0\.0\.0\.0|[a-zA-Z0-9.-]+)$", host):
50
+ raise ValueError(f"Invalid host: {host}. Must be localhost, IP address, or valid hostname")
51
+
52
+ # Validate port parameter
53
+ if not isinstance(port, int) or not (1 <= port <= 65535):
54
+ raise ValueError(f"Invalid port: {port}. Must be integer between 1 and 65535")
55
+
56
+ # Configure tracing environment variables if enabled
57
+ if tracing_enabled:
58
+ import os
59
+
60
+ # Only set if not already configured by user
61
+ if not os.environ.get("ENABLE_OTEL"):
62
+ os.environ["ENABLE_OTEL"] = "true"
63
+ logger.info("Set ENABLE_OTEL=true for tracing")
64
+
65
+ if not os.environ.get("ENABLE_SENSITIVE_DATA"):
66
+ os.environ["ENABLE_SENSITIVE_DATA"] = "true"
67
+ logger.info("Set ENABLE_SENSITIVE_DATA=true for tracing")
68
+
69
+ if not os.environ.get("OTLP_ENDPOINT"):
70
+ os.environ["OTLP_ENDPOINT"] = "http://localhost:4317"
71
+ logger.info("Set OTLP_ENDPOINT=http://localhost:4317 for tracing")
72
+
73
+ # Create server with direct parameters
74
+ server = DevServer(
75
+ entities_dir=entities_dir, port=port, host=host, cors_origins=cors_origins, ui_enabled=ui_enabled
76
+ )
77
+
78
+ # Register in-memory entities if provided
79
+ if entities:
80
+ logger.info(f"Registering {len(entities)} in-memory entities")
81
+ # Store entities for later registration during server startup
82
+ server._pending_entities = entities
83
+
84
+ app = server.get_app()
85
+
86
+ if auto_open:
87
+
88
+ def open_browser() -> None:
89
+ import http.client
90
+ import re
91
+ import time
92
+
93
+ # Validate host and port for security
94
+ if not re.match(r"^(localhost|127\.0\.0\.1|0\.0\.0\.0|[a-zA-Z0-9.-]+)$", host):
95
+ logger.warning(f"Invalid host for auto-open: {host}")
96
+ return
97
+
98
+ if not isinstance(port, int) or not (1 <= port <= 65535):
99
+ logger.warning(f"Invalid port for auto-open: {port}")
100
+ return
101
+
102
+ # Wait for server to be ready by checking health endpoint
103
+ browser_url = f"http://{host}:{port}"
104
+
105
+ for _ in range(30): # 15 second timeout (30 * 0.5s)
106
+ try:
107
+ # Use http.client for safe connection handling (standard library)
108
+ conn = http.client.HTTPConnection(host, port, timeout=1)
109
+ try:
110
+ conn.request("GET", "/health")
111
+ response = conn.getresponse()
112
+ if response.status == 200:
113
+ webbrowser.open(browser_url)
114
+ return
115
+ finally:
116
+ conn.close()
117
+ except (http.client.HTTPException, OSError, TimeoutError):
118
+ pass
119
+ time.sleep(0.5)
120
+
121
+ # Fallback: open browser anyway after timeout
122
+ webbrowser.open(browser_url)
123
+
124
+ import threading
125
+
126
+ threading.Thread(target=open_browser, daemon=True).start()
127
+
128
+ logger.info(f"Starting Agent Framework DevUI on {host}:{port}")
129
+ uvicorn.run(app, host=host, port=port, log_level="info")
130
+
131
+
132
+ def main() -> None:
133
+ """CLI entry point for devui command."""
134
+ from ._cli import main as cli_main
135
+
136
+ cli_main()
137
+
138
+
139
+ # Export main public API
140
+ __all__ = [
141
+ "AgentFrameworkRequest",
142
+ "DevServer",
143
+ "DiscoveryResponse",
144
+ "EntityInfo",
145
+ "EnvVarRequirement",
146
+ "OpenAIError",
147
+ "OpenAIResponse",
148
+ "ResponseStreamEvent",
149
+ "main",
150
+ "serve",
151
+ ]
@@ -0,0 +1,143 @@
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("--version", action="version", version=f"Agent Framework DevUI {get_version()}")
59
+
60
+ return parser
61
+
62
+
63
+ def get_version() -> str:
64
+ """Get the package version."""
65
+ try:
66
+ from . import __version__
67
+
68
+ return __version__
69
+ except ImportError:
70
+ return "unknown"
71
+
72
+
73
+ def validate_directory(directory: str) -> str:
74
+ """Validate and normalize the entities directory."""
75
+ if not directory:
76
+ directory = "."
77
+
78
+ abs_dir = os.path.abspath(directory)
79
+
80
+ if not os.path.exists(abs_dir):
81
+ print(f"❌ Error: Directory '{directory}' does not exist", file=sys.stderr) # noqa: T201
82
+ sys.exit(1)
83
+
84
+ if not os.path.isdir(abs_dir):
85
+ print(f"❌ Error: '{directory}' is not a directory", file=sys.stderr) # noqa: T201
86
+ sys.exit(1)
87
+
88
+ return abs_dir
89
+
90
+
91
+ def print_startup_info(entities_dir: str, host: str, port: int, ui_enabled: bool, reload: bool) -> None:
92
+ """Print startup information."""
93
+ print("🤖 Agent Framework DevUI") # noqa: T201
94
+ print("=" * 50) # noqa: T201
95
+ print(f"📁 Entities directory: {entities_dir}") # noqa: T201
96
+ print(f"🌐 Server URL: http://{host}:{port}") # noqa: T201
97
+ print(f"🎨 UI enabled: {'Yes' if ui_enabled else 'No'}") # noqa: T201
98
+ print(f"🔄 Auto-reload: {'Yes' if reload else 'No'}") # noqa: T201
99
+ print("=" * 50) # noqa: T201
100
+ print("🔍 Scanning for entities...") # noqa: T201
101
+
102
+
103
+ def main() -> None:
104
+ """Main CLI entry point."""
105
+ parser = create_cli_parser()
106
+ args = parser.parse_args()
107
+
108
+ # Setup logging
109
+ setup_logging(args.log_level)
110
+
111
+ # Validate directory
112
+ entities_dir = validate_directory(args.directory)
113
+
114
+ # Extract parameters directly from args
115
+ ui_enabled = not args.headless
116
+
117
+ # Print startup info
118
+ print_startup_info(entities_dir, args.host, args.port, ui_enabled, args.reload)
119
+
120
+ # Import and start server
121
+ try:
122
+ from . import serve
123
+
124
+ serve(
125
+ entities_dir=entities_dir,
126
+ port=args.port,
127
+ host=args.host,
128
+ auto_open=not args.no_open,
129
+ ui_enabled=ui_enabled,
130
+ tracing_enabled=args.tracing,
131
+ )
132
+
133
+ except KeyboardInterrupt:
134
+ print("\n👋 Shutting down Agent Framework DevUI...") # noqa: T201
135
+ sys.exit(0)
136
+ except Exception as e:
137
+ logger.exception("Failed to start server")
138
+ print(f"❌ Error: {e}", file=sys.stderr) # noqa: T201
139
+ sys.exit(1)
140
+
141
+
142
+ if __name__ == "__main__":
143
+ main()