langgraph-agent-toolkit 0.1.0__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.
Files changed (74) hide show
  1. langgraph_agent_toolkit/__init__.py +7 -0
  2. langgraph_agent_toolkit/agents/__init__.py +0 -0
  3. langgraph_agent_toolkit/agents/agent.py +14 -0
  4. langgraph_agent_toolkit/agents/agent_executor.py +415 -0
  5. langgraph_agent_toolkit/agents/blueprints/__init__.py +0 -0
  6. langgraph_agent_toolkit/agents/blueprints/bg_task_agent/__init__.py +0 -0
  7. langgraph_agent_toolkit/agents/blueprints/bg_task_agent/agent.py +69 -0
  8. langgraph_agent_toolkit/agents/blueprints/bg_task_agent/task.py +52 -0
  9. langgraph_agent_toolkit/agents/blueprints/bg_task_agent/utils.py +17 -0
  10. langgraph_agent_toolkit/agents/blueprints/chatbot/__init__.py +0 -0
  11. langgraph_agent_toolkit/agents/blueprints/chatbot/agent.py +34 -0
  12. langgraph_agent_toolkit/agents/blueprints/command_agent/__init__.py +0 -0
  13. langgraph_agent_toolkit/agents/blueprints/command_agent/agent.py +54 -0
  14. langgraph_agent_toolkit/agents/blueprints/interrupt_agent/__init__.py +0 -0
  15. langgraph_agent_toolkit/agents/blueprints/interrupt_agent/agent.py +140 -0
  16. langgraph_agent_toolkit/agents/blueprints/react/__init__.py +0 -0
  17. langgraph_agent_toolkit/agents/blueprints/react/agent.py +67 -0
  18. langgraph_agent_toolkit/agents/blueprints/react_so/__init__.py +0 -0
  19. langgraph_agent_toolkit/agents/blueprints/react_so/agent.py +39 -0
  20. langgraph_agent_toolkit/agents/blueprints/supervisor_agent/__init__.py +0 -0
  21. langgraph_agent_toolkit/agents/blueprints/supervisor_agent/agent.py +44 -0
  22. langgraph_agent_toolkit/agents/components/__init__.py +0 -0
  23. langgraph_agent_toolkit/agents/components/creators/__init__.py +4 -0
  24. langgraph_agent_toolkit/agents/components/creators/create_react_agent.py +459 -0
  25. langgraph_agent_toolkit/agents/components/tools.py +13 -0
  26. langgraph_agent_toolkit/agents/components/utils.py +42 -0
  27. langgraph_agent_toolkit/client/__init__.py +4 -0
  28. langgraph_agent_toolkit/client/client.py +344 -0
  29. langgraph_agent_toolkit/core/__init__.py +5 -0
  30. langgraph_agent_toolkit/core/memory/__init__.py +0 -0
  31. langgraph_agent_toolkit/core/memory/base.py +33 -0
  32. langgraph_agent_toolkit/core/memory/factory.py +30 -0
  33. langgraph_agent_toolkit/core/memory/postgres.py +76 -0
  34. langgraph_agent_toolkit/core/memory/sqlite.py +21 -0
  35. langgraph_agent_toolkit/core/memory/types.py +6 -0
  36. langgraph_agent_toolkit/core/models/__init__.py +5 -0
  37. langgraph_agent_toolkit/core/models/chat_openai.py +21 -0
  38. langgraph_agent_toolkit/core/models/factory.py +118 -0
  39. langgraph_agent_toolkit/core/models/fake.py +25 -0
  40. langgraph_agent_toolkit/core/observability/__init__.py +10 -0
  41. langgraph_agent_toolkit/core/observability/base.py +331 -0
  42. langgraph_agent_toolkit/core/observability/empty.py +67 -0
  43. langgraph_agent_toolkit/core/observability/factory.py +43 -0
  44. langgraph_agent_toolkit/core/observability/langfuse.py +118 -0
  45. langgraph_agent_toolkit/core/observability/langsmith.py +131 -0
  46. langgraph_agent_toolkit/core/observability/types.py +34 -0
  47. langgraph_agent_toolkit/core/prompts/__init__.py +0 -0
  48. langgraph_agent_toolkit/core/prompts/chat_prompt_template.py +528 -0
  49. langgraph_agent_toolkit/core/settings.py +164 -0
  50. langgraph_agent_toolkit/helper/__init__.py +0 -0
  51. langgraph_agent_toolkit/helper/constants.py +10 -0
  52. langgraph_agent_toolkit/helper/logging.py +111 -0
  53. langgraph_agent_toolkit/helper/types.py +7 -0
  54. langgraph_agent_toolkit/helper/utils.py +80 -0
  55. langgraph_agent_toolkit/run_agent.py +68 -0
  56. langgraph_agent_toolkit/run_client.py +55 -0
  57. langgraph_agent_toolkit/run_service.py +19 -0
  58. langgraph_agent_toolkit/schema/__init__.py +28 -0
  59. langgraph_agent_toolkit/schema/models.py +25 -0
  60. langgraph_agent_toolkit/schema/schema.py +210 -0
  61. langgraph_agent_toolkit/schema/task_data.py +72 -0
  62. langgraph_agent_toolkit/service/__init__.py +0 -0
  63. langgraph_agent_toolkit/service/exception_handlers.py +46 -0
  64. langgraph_agent_toolkit/service/factory.py +213 -0
  65. langgraph_agent_toolkit/service/handler.py +122 -0
  66. langgraph_agent_toolkit/service/middleware.py +18 -0
  67. langgraph_agent_toolkit/service/routes.py +169 -0
  68. langgraph_agent_toolkit/service/types.py +8 -0
  69. langgraph_agent_toolkit/service/utils.py +136 -0
  70. langgraph_agent_toolkit/streamlit_app.py +368 -0
  71. langgraph_agent_toolkit-0.1.0.dist-info/METADATA +424 -0
  72. langgraph_agent_toolkit-0.1.0.dist-info/RECORD +74 -0
  73. langgraph_agent_toolkit-0.1.0.dist-info/WHEEL +4 -0
  74. langgraph_agent_toolkit-0.1.0.dist-info/licenses/LICENSE +21 -0
File without changes
@@ -0,0 +1,46 @@
1
+ import traceback
2
+
3
+ from fastapi import FastAPI, Request, status
4
+ from fastapi.responses import JSONResponse
5
+
6
+ from langgraph_agent_toolkit.helper.logging import logger
7
+
8
+
9
+ async def handle_value_error(request: Request, exc: ValueError) -> JSONResponse:
10
+ """Handle ValueError exceptions."""
11
+ logger.error(f"ValueError: {exc}")
12
+ return JSONResponse(
13
+ status_code=status.HTTP_400_BAD_REQUEST,
14
+ content={"detail": str(exc)},
15
+ )
16
+
17
+
18
+ async def handle_message_conversion_error(request: Request, exc: Exception) -> JSONResponse:
19
+ """Handle errors when converting between message types."""
20
+ if isinstance(exc, ValueError) and "Unsupported message type" in str(exc):
21
+ logger.error(f"Message conversion error: {exc}")
22
+ return JSONResponse(
23
+ status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
24
+ content={"detail": str(exc)},
25
+ )
26
+
27
+ raise exc
28
+
29
+
30
+ async def handle_unexpected_error(request: Request, exc: Exception) -> JSONResponse:
31
+ """Handle unexpected exceptions."""
32
+ error_detail = f"{exc.__class__.__name__}: {exc}"
33
+ logger.error(f"Unexpected error: {error_detail}")
34
+ logger.error(traceback.format_exc())
35
+ return JSONResponse(
36
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
37
+ content={"detail": f"An unexpected error occurred: {str(exc)}"},
38
+ )
39
+
40
+
41
+ def register_exception_handlers(app: FastAPI) -> None:
42
+ """Register all exception handlers to the FastAPI app."""
43
+ app.exception_handler(ValueError)(handle_value_error)
44
+ app.exception_handler(Exception)(handle_unexpected_error)
45
+ # Register the message conversion error handler
46
+ app.add_exception_handler(ValueError, handle_message_conversion_error)
@@ -0,0 +1,213 @@
1
+ import asyncio
2
+ import json
3
+ import os
4
+ import sys
5
+ from typing import Any, Dict, Optional
6
+
7
+ import uvicorn
8
+ from fastapi import FastAPI
9
+
10
+ from langgraph_agent_toolkit.core import settings as base_settings
11
+ from langgraph_agent_toolkit.helper.logging import logger
12
+ from langgraph_agent_toolkit.service.handler import create_app
13
+ from langgraph_agent_toolkit.service.types import RunnerType
14
+ from langgraph_agent_toolkit.service.utils import setup_logging
15
+
16
+
17
+ class ServiceRunner:
18
+ """A factory class to run the API service in different ways.
19
+
20
+ This class provides methods to run the service with different runners:
21
+ - With Uvicorn
22
+ - With Gunicorn
23
+ - With Mangum (AWS Lambda)
24
+ - With Azure Functions
25
+ """
26
+
27
+ def __init__(self, custom_settings: Optional[Dict[str, Any]] = None):
28
+ """Initialize the ServiceRunner.
29
+
30
+ Args:
31
+ custom_settings: Optional dictionary of settings to override the default settings.
32
+
33
+ """
34
+ _ = setup_logging()
35
+
36
+ # Override global settings if provided
37
+ if custom_settings:
38
+ for key, value in custom_settings.items():
39
+ if hasattr(base_settings, key):
40
+ # Set the value in the global settings object
41
+ setattr(base_settings, key, value)
42
+ logger.info(f"Overriding setting {key}")
43
+
44
+ # Also set it as an environment variable for child processes
45
+ # Handle different types appropriately for environment variables
46
+ env_value = None
47
+ if isinstance(value, list):
48
+ # Convert lists to JSON strings
49
+ env_value = json.dumps(value)
50
+ elif isinstance(value, bool):
51
+ # Convert booleans to string "True" or "False"
52
+ env_value = str(value)
53
+ elif value is not None:
54
+ # Convert other values to strings
55
+ env_value = str(value)
56
+
57
+ if env_value is not None:
58
+ os.environ[f"LANGGRAPH_{key}"] = env_value
59
+ logger.info(f"Set environment variable LANGGRAPH_{key}={env_value}")
60
+ else:
61
+ logger.warning(f"Setting {key} not found in settings")
62
+
63
+ self.app = create_app()
64
+
65
+ def run_uvicorn(self):
66
+ """Run the API service with uvicorn."""
67
+ # Set Compatible event loop policy on Windows Systems.
68
+ if sys.platform == "win32":
69
+ asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
70
+
71
+ # In development mode with reload=True, we need to use an import string
72
+ # instead of passing the app instance directly
73
+ if base_settings.is_dev():
74
+ logger.info("Starting in development mode with hot reload enabled")
75
+ uvicorn.run(
76
+ "langgraph_agent_toolkit.service.handler:create_app",
77
+ host=base_settings.HOST,
78
+ port=base_settings.PORT,
79
+ reload=True,
80
+ factory=True,
81
+ )
82
+ else:
83
+ # In production mode, use the app instance directly
84
+ uvicorn.run(
85
+ self.app,
86
+ host=base_settings.HOST,
87
+ port=base_settings.PORT,
88
+ reload=False,
89
+ )
90
+
91
+ def run_gunicorn(self, workers: int = 4):
92
+ """Run the API service with gunicorn.
93
+
94
+ Args:
95
+ workers: Number of worker processes.
96
+
97
+ """
98
+ try:
99
+ from gunicorn.app.base import BaseApplication
100
+
101
+ class GunicornApp(BaseApplication):
102
+ def __init__(self, app, options=None):
103
+ self.options = options or {}
104
+ self.application = app
105
+ super().__init__()
106
+
107
+ def load_config(self):
108
+ for key, value in self.options.items():
109
+ self.cfg.set(key, value)
110
+
111
+ def load(self):
112
+ return self.application
113
+
114
+ options = {
115
+ "bind": f"{base_settings.HOST}:{base_settings.PORT}",
116
+ "workers": workers,
117
+ "worker_class": "uvicorn.workers.UvicornWorker",
118
+ }
119
+
120
+ GunicornApp(self.app, options).run()
121
+ except ImportError:
122
+ logger.error("Gunicorn not installed. Install it with 'pip install gunicorn'")
123
+ sys.exit(1)
124
+
125
+ def run_aws_lambda(self):
126
+ """Prepare the API service for AWS Lambda."""
127
+ try:
128
+ from mangum import Mangum
129
+
130
+ return Mangum(self.app)
131
+ except ImportError:
132
+ logger.error("Mangum not installed. Install it with 'pip install mangum'")
133
+ sys.exit(1)
134
+
135
+ def run_azure_functions(self):
136
+ """Prepare the API service for Azure Functions."""
137
+ try:
138
+ import azure.functions as func
139
+
140
+ async def main(req: func.HttpRequest) -> func.HttpResponse:
141
+ # Process the request through ASGI app
142
+ return await self._handle_azure_request(self.app, req)
143
+
144
+ return main
145
+ except ImportError:
146
+ logger.error("Azure Functions package not installed. Install with 'pip install azure-functions'")
147
+ sys.exit(1)
148
+
149
+ @staticmethod
150
+ async def _handle_azure_request(app: FastAPI, req: "func.HttpRequest") -> "func.HttpResponse":
151
+ """Handle Azure Functions HTTP request."""
152
+ import azure.functions as func
153
+
154
+ # Convert request to ASGI format
155
+ scope = {
156
+ "type": "http",
157
+ "http_version": "1.1",
158
+ "method": req.method,
159
+ "path": req.url.path,
160
+ "query_string": req.url.query.encode(),
161
+ "headers": [(k.encode(), v.encode()) for k, v in req.headers.items()],
162
+ }
163
+
164
+ # Create response container
165
+ response_body = []
166
+ response_status = None
167
+ response_headers = []
168
+
169
+ async def receive():
170
+ return {"type": "http.request", "body": req.get_body() or b""}
171
+
172
+ async def send(message):
173
+ nonlocal response_status, response_headers
174
+
175
+ if message["type"] == "http.response.start":
176
+ response_status = message["status"]
177
+ response_headers = message["headers"]
178
+ elif message["type"] == "http.response.body":
179
+ response_body.append(message["body"])
180
+
181
+ # Process the request through ASGI app
182
+ await app(scope, receive, send)
183
+
184
+ # Build Azure Functions response
185
+ headers = {k.decode(): v.decode() for k, v in response_headers}
186
+ body = b"".join(response_body)
187
+
188
+ return func.HttpResponse(
189
+ body=body,
190
+ status_code=response_status,
191
+ headers=headers,
192
+ )
193
+
194
+ def run(self, runner_type: RunnerType = RunnerType.UVICORN, **kwargs):
195
+ """Run the API service with the specified runner type.
196
+
197
+ Args:
198
+ runner_type: The type of runner to use.
199
+ **kwargs: Additional arguments to pass to the runner.
200
+
201
+ """
202
+ match runner_type:
203
+ case RunnerType.UVICORN:
204
+ self.run_uvicorn()
205
+ case RunnerType.GUNICORN:
206
+ workers = kwargs.get("workers", 4)
207
+ self.run_gunicorn(workers=workers)
208
+ case RunnerType.AWS_LAMBDA:
209
+ return self.run_aws_lambda()
210
+ case RunnerType.AZURE_FUNCTIONS:
211
+ return self.run_azure_functions()
212
+ case _:
213
+ raise ValueError(f"Unknown runner type: {runner_type}")
@@ -0,0 +1,122 @@
1
+ import warnings
2
+ from collections.abc import AsyncGenerator
3
+ from contextlib import asynccontextmanager
4
+
5
+ from fastapi import Depends, FastAPI
6
+ from langchain_core._api import LangChainBetaWarning
7
+
8
+ from langgraph_agent_toolkit import __version__
9
+ from langgraph_agent_toolkit.agents.agent_executor import AgentExecutor
10
+ from langgraph_agent_toolkit.core.memory.factory import MemoryFactory
11
+ from langgraph_agent_toolkit.core.observability.empty import EmptyObservability
12
+ from langgraph_agent_toolkit.core.observability.factory import ObservabilityFactory
13
+ from langgraph_agent_toolkit.core.observability.types import ObservabilityBackend
14
+ from langgraph_agent_toolkit.core.settings import settings
15
+ from langgraph_agent_toolkit.helper.logging import logger
16
+ from langgraph_agent_toolkit.service.exception_handlers import register_exception_handlers
17
+ from langgraph_agent_toolkit.service.middleware import LoggingMiddleware
18
+ from langgraph_agent_toolkit.service.routes import private_router, public_router
19
+ from langgraph_agent_toolkit.service.utils import verify_bearer
20
+
21
+
22
+ warnings.filterwarnings("ignore", category=LangChainBetaWarning)
23
+
24
+
25
+ @asynccontextmanager
26
+ async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
27
+ """Create a lifespan context manager for the FastAPI app."""
28
+ observability = None
29
+ initialized_agents = []
30
+
31
+ try:
32
+ # Initialize observability platform
33
+ try:
34
+ observability = ObservabilityFactory.create(settings.OBSERVABILITY_BACKEND or ObservabilityBackend.EMPTY)
35
+ logger.info(f"Initialized observability backend: {settings.OBSERVABILITY_BACKEND}")
36
+ except Exception as e:
37
+ logger.error(f"Failed to initialize observability backend: {e}")
38
+ observability = EmptyObservability()
39
+
40
+ # Initialize memory backend
41
+ try:
42
+ memory_backend = MemoryFactory.create(settings.MEMORY_BACKEND)
43
+ logger.info(f"Initialized memory backend: {settings.MEMORY_BACKEND}")
44
+ except Exception as e:
45
+ logger.error(f"Failed to initialize memory backend: {e}")
46
+ yield
47
+ return
48
+
49
+ # Initialize agent executor
50
+ try:
51
+ executor = AgentExecutor(*settings.AGENT_PATHS)
52
+ logger.info(f"Initialized AgentExecutor: {settings.AGENT_PATHS}")
53
+ # Store the executor in app.state for access in routes
54
+ app.state.agent_executor = executor
55
+ except Exception as e:
56
+ logger.error(f"Failed to initialize AgentExecutor: {e}")
57
+ yield
58
+ return
59
+
60
+ checkpoint = memory_backend.get_checkpoint_saver()
61
+ async with checkpoint as saver:
62
+ try:
63
+ await saver.setup()
64
+ agents = executor.get_all_agent_info()
65
+
66
+ if not agents:
67
+ logger.warning("No agents found in the executor.")
68
+
69
+ for a in agents:
70
+ try:
71
+ agent = executor.get_agent(a.key)
72
+ agent.graph.checkpointer = saver
73
+
74
+ # do not set up observability if it's already set
75
+ if not agent.observability:
76
+ agent.observability = observability
77
+
78
+ initialized_agents.append(a.key)
79
+ logger.info(f"Successfully initialized agent: {a.key}")
80
+ except Exception as e:
81
+ logger.error(f"Error setting up agent {a.key}: {e}")
82
+
83
+ if initialized_agents:
84
+ logger.info(f"Successfully initialized {len(initialized_agents)} agents")
85
+ else:
86
+ logger.warning("No agents were successfully initialized")
87
+
88
+ yield
89
+ except Exception as e:
90
+ logger.error(f"Error during database setup: {e}")
91
+ yield
92
+ except Exception as e:
93
+ logger.error(f"Error during initialization: {e}")
94
+ yield
95
+ finally:
96
+ if observability:
97
+ try:
98
+ logger.info("Closing observability platform...")
99
+ observability.before_shutdown()
100
+ except Exception as e:
101
+ logger.error(f"Error closing observability: {e}")
102
+
103
+
104
+ def create_app() -> FastAPI:
105
+ """Create and configure the FastAPI application."""
106
+ logger.info(f"Initializing API service v{__version__}")
107
+
108
+ app = FastAPI(lifespan=lifespan)
109
+
110
+ # add middleware
111
+ app.add_middleware(LoggingMiddleware)
112
+
113
+ # Register exception handlers
114
+ register_exception_handlers(app)
115
+
116
+ # Include public router without authentication
117
+ app.include_router(public_router)
118
+
119
+ # Include private router with authentication
120
+ app.include_router(private_router, dependencies=[Depends(verify_bearer)])
121
+
122
+ return app
@@ -0,0 +1,18 @@
1
+ from http.client import responses
2
+
3
+ from fastapi import Request
4
+ from starlette.middleware.base import BaseHTTPMiddleware
5
+
6
+ from langgraph_agent_toolkit.helper.logging import logger
7
+
8
+
9
+ class LoggingMiddleware(BaseHTTPMiddleware):
10
+ """Middleware to log incoming requests and outgoing responses."""
11
+
12
+ async def dispatch(self, request: Request, call_next):
13
+ logger.info(f"HTTP Request: {request.method} {request.url}")
14
+ response = await call_next(request)
15
+ logger.info(
16
+ f'HTTP Response: {request.method} {request.url} "{response.status_code} {responses[response.status_code]}"'
17
+ )
18
+ return response
@@ -0,0 +1,169 @@
1
+ from fastapi import APIRouter, HTTPException, Request, status
2
+ from fastapi.responses import RedirectResponse, StreamingResponse
3
+ from langchain_core.messages import AnyMessage
4
+ from langchain_core.runnables import RunnableConfig
5
+ from langgraph.graph.state import CompiledStateGraph
6
+
7
+ from langgraph_agent_toolkit import __version__
8
+ from langgraph_agent_toolkit.agents.agent import Agent
9
+ from langgraph_agent_toolkit.core import settings
10
+ from langgraph_agent_toolkit.helper.constants import DEFAULT_AGENT
11
+ from langgraph_agent_toolkit.helper.logging import logger
12
+ from langgraph_agent_toolkit.helper.utils import langchain_to_chat_message
13
+ from langgraph_agent_toolkit.schema import (
14
+ ChatHistory,
15
+ ChatHistoryInput,
16
+ ChatMessage,
17
+ Feedback,
18
+ FeedbackResponse,
19
+ HealthCheck,
20
+ ServiceMetadata,
21
+ StreamInput,
22
+ UserInput,
23
+ )
24
+ from langgraph_agent_toolkit.service.utils import (
25
+ _sse_response_example,
26
+ get_agent,
27
+ get_agent_executor,
28
+ get_all_agent_info,
29
+ message_generator,
30
+ )
31
+
32
+
33
+ # Create separate routers for private and public endpoints
34
+ private_router = APIRouter()
35
+ public_router = APIRouter()
36
+
37
+
38
+ @private_router.get("/info", tags=["info"])
39
+ async def info(request: Request) -> ServiceMetadata:
40
+ models = list(settings.AVAILABLE_MODELS)
41
+ models.sort()
42
+ return ServiceMetadata(
43
+ agents=get_all_agent_info(request),
44
+ models=models,
45
+ default_agent=DEFAULT_AGENT,
46
+ default_model=settings.DEFAULT_MODEL,
47
+ )
48
+
49
+
50
+ @private_router.post("/{agent_id}/invoke", tags=["agent"])
51
+ @private_router.post("/invoke", tags=["agent"])
52
+ async def invoke(user_input: UserInput, agent_id: str = DEFAULT_AGENT, request: Request = None) -> ChatMessage:
53
+ """Invoke an agent with user input to retrieve a final response.
54
+
55
+ If agent_id is not provided, the default agent will be used.
56
+ Use thread_id to persist and continue a multi-turn conversation. run_id kwarg
57
+ is also attached to messages for recording feedback.
58
+ """
59
+ executor = get_agent_executor(request)
60
+
61
+ try:
62
+ return await executor.invoke(
63
+ agent_id=agent_id,
64
+ message=user_input.message,
65
+ thread_id=user_input.thread_id,
66
+ model=user_input.model,
67
+ agent_config=user_input.agent_config,
68
+ recursion_limit=user_input.recursion_limit,
69
+ )
70
+ except Exception as e:
71
+ logger.error(f"An exception occurred: {e}")
72
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Unexpected error")
73
+
74
+
75
+ @private_router.post(
76
+ "/{agent_id}/stream",
77
+ response_class=StreamingResponse,
78
+ responses=_sse_response_example(),
79
+ tags=["agent"],
80
+ )
81
+ @private_router.post(
82
+ "/stream",
83
+ response_class=StreamingResponse,
84
+ responses=_sse_response_example(),
85
+ tags=["agent"],
86
+ )
87
+ async def stream(user_input: StreamInput, agent_id: str = DEFAULT_AGENT, request: Request = None) -> StreamingResponse:
88
+ """Stream an agent's response to a user input, including intermediate messages and tokens.
89
+
90
+ If agent_id is not provided, the default agent will be used.
91
+ Use thread_id to persist and continue a multi-turn conversation. run_id kwarg
92
+ is also attached to all messages for recording feedback.
93
+
94
+ Set `stream_tokens=false` to return intermediate messages but not token-by-token.
95
+ """
96
+ return StreamingResponse(
97
+ message_generator(user_input, request, agent_id),
98
+ media_type="text/event-stream",
99
+ )
100
+
101
+
102
+ @private_router.post("/feedback", status_code=status.HTTP_201_CREATED, tags=["feedback"])
103
+ async def feedback(feedback: Feedback, agent_id: str = DEFAULT_AGENT, request: Request = None) -> FeedbackResponse:
104
+ """Record feedback for a run to the configured observability platform.
105
+
106
+ This routes the feedback to the appropriate platform based on the agent's configuration.
107
+ """
108
+ try:
109
+ agent = get_agent(request, agent_id)
110
+ agent.observability.record_feedback(
111
+ run_id=feedback.run_id,
112
+ key=feedback.key,
113
+ score=feedback.score,
114
+ **feedback.kwargs,
115
+ )
116
+
117
+ return FeedbackResponse(
118
+ run_id=feedback.run_id,
119
+ message=f"Feedback '{feedback.key}' recorded successfully for run {feedback.run_id}.",
120
+ )
121
+ except ValueError as e:
122
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
123
+ except Exception as e:
124
+ logger.error(f"An exception occurred while recording feedback: {e}")
125
+ raise HTTPException(
126
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Unexpected error recording feedback"
127
+ )
128
+
129
+
130
+ @private_router.post("/history", tags=["history"])
131
+ def history(input: ChatHistoryInput, request: Request = None) -> ChatHistory:
132
+ """Get chat history."""
133
+ agent: Agent = get_agent(request, DEFAULT_AGENT)
134
+ try:
135
+ agent_graph: CompiledStateGraph = agent.graph
136
+ state_snapshot = agent_graph.get_state(
137
+ config=RunnableConfig(
138
+ configurable={
139
+ "thread_id": input.thread_id,
140
+ }
141
+ )
142
+ )
143
+ messages: list[AnyMessage] = state_snapshot.values["messages"]
144
+ chat_messages: list[ChatMessage] = [langchain_to_chat_message(m) for m in messages]
145
+ return ChatHistory(messages=chat_messages)
146
+ except Exception as e:
147
+ logger.error(f"An exception occurred: {e}")
148
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Unexpected error")
149
+
150
+
151
+ @public_router.get("/", tags=["public"])
152
+ async def redirect_to_docs() -> RedirectResponse:
153
+ return RedirectResponse(url="/docs")
154
+
155
+
156
+ @public_router.get(
157
+ "/health",
158
+ tags=["public", "healthcheck"],
159
+ summary="Perform a Health Check",
160
+ response_description="Return HTTP Status Code 200 (OK)",
161
+ status_code=status.HTTP_200_OK,
162
+ response_model=HealthCheck,
163
+ )
164
+ async def health_check() -> HealthCheck:
165
+ """Health check endpoint."""
166
+ return HealthCheck(
167
+ content="healthy",
168
+ version=__version__,
169
+ )
@@ -0,0 +1,8 @@
1
+ from enum import StrEnum, auto
2
+
3
+
4
+ class RunnerType(StrEnum):
5
+ UVICORN = auto()
6
+ GUNICORN = auto()
7
+ AWS_LAMBDA = auto()
8
+ AZURE_FUNCTIONS = auto()