blaxel 0.64.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.
- blaxel/__init__.py +8 -0
- blaxel/agents/__init__.py +5 -0
- blaxel/agents/chain.py +153 -0
- blaxel/agents/chat.py +286 -0
- blaxel/agents/decorator.py +208 -0
- blaxel/agents/thread.py +24 -0
- blaxel/agents/voice/openai.py +255 -0
- blaxel/agents/voice/utils.py +25 -0
- blaxel/api/__init__.py +1 -0
- blaxel/api/agents/__init__.py +0 -0
- blaxel/api/agents/create_agent.py +155 -0
- blaxel/api/agents/delete_agent.py +146 -0
- blaxel/api/agents/get_agent.py +146 -0
- blaxel/api/agents/get_agent_logs.py +151 -0
- blaxel/api/agents/get_agent_metrics.py +150 -0
- blaxel/api/agents/get_agent_trace_ids.py +201 -0
- blaxel/api/agents/list_agent_revisions.py +155 -0
- blaxel/api/agents/list_agents.py +127 -0
- blaxel/api/agents/update_agent.py +168 -0
- blaxel/api/configurations/__init__.py +0 -0
- blaxel/api/configurations/get_configuration.py +122 -0
- blaxel/api/default/__init__.py +0 -0
- blaxel/api/default/get_trace.py +150 -0
- blaxel/api/default/get_trace_ids.py +218 -0
- blaxel/api/default/get_trace_logs.py +186 -0
- blaxel/api/default/list_mcp_hub_definitions.py +127 -0
- blaxel/api/functions/__init__.py +0 -0
- blaxel/api/functions/create_function.py +155 -0
- blaxel/api/functions/delete_function.py +146 -0
- blaxel/api/functions/get_function.py +146 -0
- blaxel/api/functions/get_function_logs.py +151 -0
- blaxel/api/functions/get_function_metrics.py +150 -0
- blaxel/api/functions/get_function_trace_ids.py +201 -0
- blaxel/api/functions/list_function_revisions.py +158 -0
- blaxel/api/functions/list_functions.py +131 -0
- blaxel/api/functions/update_function.py +168 -0
- blaxel/api/integrations/__init__.py +0 -0
- blaxel/api/integrations/create_integration_connection.py +167 -0
- blaxel/api/integrations/delete_integration_connection.py +158 -0
- blaxel/api/integrations/get_integration.py +97 -0
- blaxel/api/integrations/get_integration_connection.py +158 -0
- blaxel/api/integrations/get_integration_connection_model.py +104 -0
- blaxel/api/integrations/get_integration_connection_model_endpoint_configurations.py +97 -0
- blaxel/api/integrations/list_integration_connection_models.py +97 -0
- blaxel/api/integrations/list_integration_connections.py +139 -0
- blaxel/api/integrations/update_integration_connection.py +180 -0
- blaxel/api/invitations/__init__.py +0 -0
- blaxel/api/invitations/list_all_pending_invitations.py +142 -0
- blaxel/api/knowledgebases/__init__.py +0 -0
- blaxel/api/knowledgebases/create_knowledgebase.py +163 -0
- blaxel/api/knowledgebases/delete_knowledgebase.py +154 -0
- blaxel/api/knowledgebases/get_knowledgebase.py +154 -0
- blaxel/api/knowledgebases/list_knowledgebase_revisions.py +158 -0
- blaxel/api/knowledgebases/list_knowledgebases.py +139 -0
- blaxel/api/knowledgebases/update_knowledgebase.py +176 -0
- blaxel/api/locations/__init__.py +0 -0
- blaxel/api/locations/list_locations.py +139 -0
- blaxel/api/metrics/__init__.py +0 -0
- blaxel/api/metrics/get_metrics.py +130 -0
- blaxel/api/models/__init__.py +0 -0
- blaxel/api/models/create_model.py +163 -0
- blaxel/api/models/delete_model.py +154 -0
- blaxel/api/models/get_model.py +154 -0
- blaxel/api/models/get_model_logs.py +155 -0
- blaxel/api/models/get_model_metrics.py +158 -0
- blaxel/api/models/get_model_trace_ids.py +201 -0
- blaxel/api/models/list_model_revisions.py +158 -0
- blaxel/api/models/list_models.py +135 -0
- blaxel/api/models/update_model.py +176 -0
- blaxel/api/policies/__init__.py +0 -0
- blaxel/api/policies/create_policy.py +167 -0
- blaxel/api/policies/delete_policy.py +154 -0
- blaxel/api/policies/get_policy.py +154 -0
- blaxel/api/policies/list_policies.py +139 -0
- blaxel/api/policies/update_policy.py +180 -0
- blaxel/api/privateclusters/__init__.py +0 -0
- blaxel/api/privateclusters/create_private_cluster.py +132 -0
- blaxel/api/privateclusters/delete_private_cluster.py +156 -0
- blaxel/api/privateclusters/get_private_cluster.py +159 -0
- blaxel/api/privateclusters/get_private_cluster_health.py +97 -0
- blaxel/api/privateclusters/list_private_clusters.py +140 -0
- blaxel/api/privateclusters/update_private_cluster.py +156 -0
- blaxel/api/privateclusters/update_private_cluster_health.py +97 -0
- blaxel/api/service_accounts/__init__.py +0 -0
- blaxel/api/service_accounts/create_api_key_for_service_account.py +177 -0
- blaxel/api/service_accounts/create_workspace_service_account.py +170 -0
- blaxel/api/service_accounts/delete_api_key_for_service_account.py +104 -0
- blaxel/api/service_accounts/delete_workspace_service_account.py +160 -0
- blaxel/api/service_accounts/get_workspace_service_accounts.py +141 -0
- blaxel/api/service_accounts/list_api_keys_for_service_account.py +163 -0
- blaxel/api/service_accounts/update_workspace_service_account.py +183 -0
- blaxel/api/store/__init__.py +0 -0
- blaxel/api/store/get_store_agent.py +146 -0
- blaxel/api/store/get_store_function.py +146 -0
- blaxel/api/store/list_store_agents.py +131 -0
- blaxel/api/store/list_store_functions.py +131 -0
- blaxel/api/workspaces/__init__.py +0 -0
- blaxel/api/workspaces/accept_workspace_invitation.py +161 -0
- blaxel/api/workspaces/create_worspace.py +163 -0
- blaxel/api/workspaces/decline_workspace_invitation.py +158 -0
- blaxel/api/workspaces/delete_workspace.py +154 -0
- blaxel/api/workspaces/get_workspace.py +154 -0
- blaxel/api/workspaces/invite_workspace_user.py +174 -0
- blaxel/api/workspaces/leave_workspace.py +161 -0
- blaxel/api/workspaces/list_workspace_users.py +139 -0
- blaxel/api/workspaces/list_workspaces.py +139 -0
- blaxel/api/workspaces/remove_workspace_user.py +101 -0
- blaxel/api/workspaces/update_workspace.py +176 -0
- blaxel/api/workspaces/update_workspace_user_role.py +187 -0
- blaxel/authentication/__init__.py +45 -0
- blaxel/authentication/apikey.py +50 -0
- blaxel/authentication/authentication.py +176 -0
- blaxel/authentication/clientcredentials.py +103 -0
- blaxel/authentication/credentials.py +295 -0
- blaxel/authentication/device_mode.py +197 -0
- blaxel/client.py +281 -0
- blaxel/common/__init__.py +17 -0
- blaxel/common/error.py +27 -0
- blaxel/common/instrumentation.py +317 -0
- blaxel/common/logger.py +60 -0
- blaxel/common/secrets.py +39 -0
- blaxel/common/settings.py +150 -0
- blaxel/common/slugify.py +18 -0
- blaxel/common/utils.py +34 -0
- blaxel/deploy/__init__.py +8 -0
- blaxel/deploy/deploy.py +316 -0
- blaxel/deploy/format.py +46 -0
- blaxel/deploy/parser.py +192 -0
- blaxel/errors.py +16 -0
- blaxel/functions/__init__.py +7 -0
- blaxel/functions/common.py +228 -0
- blaxel/functions/decorator.py +64 -0
- blaxel/functions/local/local.py +48 -0
- blaxel/functions/mcp/client.py +96 -0
- blaxel/functions/mcp/mcp.py +168 -0
- blaxel/functions/mcp/utils.py +56 -0
- blaxel/functions/remote/remote.py +183 -0
- blaxel/models/__init__.py +233 -0
- blaxel/models/acl.py +133 -0
- blaxel/models/agent.py +126 -0
- blaxel/models/agent_chain.py +88 -0
- blaxel/models/agent_spec.py +346 -0
- blaxel/models/api_key.py +142 -0
- blaxel/models/configuration.py +85 -0
- blaxel/models/continent.py +70 -0
- blaxel/models/core_event.py +97 -0
- blaxel/models/core_spec.py +249 -0
- blaxel/models/core_spec_configurations.py +77 -0
- blaxel/models/country.py +70 -0
- blaxel/models/create_api_key_for_service_account_body.py +69 -0
- blaxel/models/create_workspace_service_account_body.py +71 -0
- blaxel/models/create_workspace_service_account_response_200.py +105 -0
- blaxel/models/delete_workspace_service_account_response_200.py +96 -0
- blaxel/models/entrypoint.py +96 -0
- blaxel/models/entrypoint_env.py +45 -0
- blaxel/models/flavor.py +70 -0
- blaxel/models/form.py +120 -0
- blaxel/models/form_config.py +45 -0
- blaxel/models/form_oauthomitempty.py +45 -0
- blaxel/models/form_secrets.py +45 -0
- blaxel/models/function.py +126 -0
- blaxel/models/function_kit.py +97 -0
- blaxel/models/function_spec.py +310 -0
- blaxel/models/get_trace_ids_response_200.py +45 -0
- blaxel/models/get_trace_logs_response_200.py +45 -0
- blaxel/models/get_trace_response_200.py +45 -0
- blaxel/models/get_workspace_service_accounts_response_200_item.py +96 -0
- blaxel/models/histogram_bucket.py +79 -0
- blaxel/models/histogram_stats.py +88 -0
- blaxel/models/integration_connection.py +96 -0
- blaxel/models/integration_connection_spec.py +114 -0
- blaxel/models/integration_connection_spec_config.py +45 -0
- blaxel/models/integration_connection_spec_secret.py +45 -0
- blaxel/models/integration_model.py +162 -0
- blaxel/models/integration_repository.py +88 -0
- blaxel/models/invite_workspace_user_body.py +60 -0
- blaxel/models/knowledgebase.py +126 -0
- blaxel/models/knowledgebase_spec.py +163 -0
- blaxel/models/knowledgebase_spec_options.py +45 -0
- blaxel/models/last_n_requests_metric.py +79 -0
- blaxel/models/latency_metric.py +144 -0
- blaxel/models/location_response.py +113 -0
- blaxel/models/mcp_definition.py +188 -0
- blaxel/models/mcp_definition_entrypoint.py +45 -0
- blaxel/models/mcp_definition_form.py +45 -0
- blaxel/models/metadata.py +139 -0
- blaxel/models/metadata_labels.py +45 -0
- blaxel/models/metric.py +79 -0
- blaxel/models/metrics.py +169 -0
- blaxel/models/metrics_models.py +45 -0
- blaxel/models/metrics_request_total_per_code.py +45 -0
- blaxel/models/metrics_rps_per_code.py +45 -0
- blaxel/models/model.py +126 -0
- blaxel/models/model_private_cluster.py +79 -0
- blaxel/models/model_spec.py +249 -0
- blaxel/models/o_auth.py +72 -0
- blaxel/models/owner_fields.py +70 -0
- blaxel/models/pending_invitation.py +124 -0
- blaxel/models/pending_invitation_accept.py +85 -0
- blaxel/models/pending_invitation_render.py +147 -0
- blaxel/models/pending_invitation_render_invited_by.py +88 -0
- blaxel/models/pending_invitation_render_workspace.py +70 -0
- blaxel/models/pending_invitation_workspace_details.py +72 -0
- blaxel/models/pod_template_spec.py +45 -0
- blaxel/models/policy.py +96 -0
- blaxel/models/policy_location.py +70 -0
- blaxel/models/policy_max_tokens.py +106 -0
- blaxel/models/policy_spec.py +151 -0
- blaxel/models/private_cluster.py +183 -0
- blaxel/models/private_location.py +61 -0
- blaxel/models/repository.py +70 -0
- blaxel/models/request_duration_over_time_metric.py +97 -0
- blaxel/models/request_duration_over_time_metrics.py +80 -0
- blaxel/models/request_total_by_origin_metric.py +115 -0
- blaxel/models/request_total_by_origin_metric_request_total_by_origin.py +45 -0
- blaxel/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +45 -0
- blaxel/models/request_total_metric.py +123 -0
- blaxel/models/request_total_metric_request_total_per_code.py +45 -0
- blaxel/models/request_total_metric_rps_per_code.py +45 -0
- blaxel/models/resource_log.py +79 -0
- blaxel/models/resource_metrics.py +270 -0
- blaxel/models/resource_metrics_request_total_per_code.py +45 -0
- blaxel/models/resource_metrics_rps_per_code.py +45 -0
- blaxel/models/revision_configuration.py +97 -0
- blaxel/models/revision_metadata.py +124 -0
- blaxel/models/runtime.py +196 -0
- blaxel/models/runtime_startup_probe.py +45 -0
- blaxel/models/serverless_config.py +80 -0
- blaxel/models/spec_configuration.py +70 -0
- blaxel/models/store_agent.py +178 -0
- blaxel/models/store_agent_labels.py +45 -0
- blaxel/models/store_configuration.py +151 -0
- blaxel/models/store_configuration_option.py +79 -0
- blaxel/models/store_function.py +211 -0
- blaxel/models/store_function_kit.py +97 -0
- blaxel/models/store_function_labels.py +45 -0
- blaxel/models/store_function_parameter.py +88 -0
- blaxel/models/time_fields.py +70 -0
- blaxel/models/token_rate_metric.py +88 -0
- blaxel/models/token_rate_metrics.py +120 -0
- blaxel/models/token_total_metric.py +106 -0
- blaxel/models/trace_ids_response.py +45 -0
- blaxel/models/update_workspace_service_account_body.py +69 -0
- blaxel/models/update_workspace_service_account_response_200.py +96 -0
- blaxel/models/update_workspace_user_role_body.py +60 -0
- blaxel/models/websocket_channel.py +88 -0
- blaxel/models/workspace.py +148 -0
- blaxel/models/workspace_labels.py +45 -0
- blaxel/models/workspace_user.py +115 -0
- blaxel/py.typed +1 -0
- blaxel/run.py +108 -0
- blaxel/serve/app.py +131 -0
- blaxel/serve/middlewares/__init__.py +10 -0
- blaxel/serve/middlewares/accesslog.py +32 -0
- blaxel/serve/middlewares/processtime.py +28 -0
- blaxel/types.py +46 -0
- blaxel-0.64.0.dist-info/METADATA +96 -0
- blaxel-0.64.0.dist-info/RECORD +261 -0
- blaxel-0.64.0.dist-info/WHEEL +4 -0
- blaxel-0.64.0.dist-info/entry_points.txt +2 -0
- blaxel-0.64.0.dist-info/licenses/LICENSE +21 -0
blaxel/__init__.py
ADDED
blaxel/agents/chain.py
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
import asyncio
|
2
|
+
import os
|
3
|
+
import warnings
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from typing import Callable
|
6
|
+
|
7
|
+
import pydantic
|
8
|
+
import typing_extensions as t
|
9
|
+
from langchain_core.tools.base import BaseTool, ToolException
|
10
|
+
|
11
|
+
from blaxel.aimon.settings import get_settings
|
12
|
+
from blaxel.api.agents import list_agents
|
13
|
+
from blaxel.authentication.authentication import AuthenticatedClient
|
14
|
+
from blaxel.models import Agent, AgentChain
|
15
|
+
from blaxel.run import RunClient
|
16
|
+
|
17
|
+
|
18
|
+
class ChainTool(BaseTool):
|
19
|
+
"""
|
20
|
+
A tool that allows chaining of agent actions. Extends LangChain's BaseTool.
|
21
|
+
"""
|
22
|
+
|
23
|
+
client: RunClient
|
24
|
+
handle_tool_error: bool | str | Callable[[ToolException], str] | None = True
|
25
|
+
_cloud: bool = False
|
26
|
+
_service_name: str | None = None
|
27
|
+
|
28
|
+
@t.override
|
29
|
+
def _run(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
30
|
+
"""
|
31
|
+
Executes the tool synchronously.
|
32
|
+
|
33
|
+
Parameters:
|
34
|
+
*args (Any): Positional arguments.
|
35
|
+
**kwargs (Any): Keyword arguments.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
Any: The result of the tool execution.
|
39
|
+
"""
|
40
|
+
warnings.warn(
|
41
|
+
"Invoke this tool asynchronousely using `ainvoke`. This method exists only to satisfy standard tests.",
|
42
|
+
stacklevel=1,
|
43
|
+
)
|
44
|
+
return asyncio.run(self._arun(*args, **kwargs))
|
45
|
+
|
46
|
+
@t.override
|
47
|
+
async def _arun(self, *args: t.Any, **kwargs: t.Any) -> t.Any:
|
48
|
+
"""
|
49
|
+
Executes the tool asynchronously.
|
50
|
+
|
51
|
+
Parameters:
|
52
|
+
*args (Any): Positional arguments.
|
53
|
+
**kwargs (Any): Keyword arguments.
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
Any: The result of the asynchronous tool execution.
|
57
|
+
"""
|
58
|
+
result = self.client.run(
|
59
|
+
"agent",
|
60
|
+
self.name,
|
61
|
+
"POST",
|
62
|
+
cloud=self._cloud,
|
63
|
+
service_name=self._service_name,
|
64
|
+
json=kwargs,
|
65
|
+
)
|
66
|
+
return result.text
|
67
|
+
|
68
|
+
@t.override
|
69
|
+
@property
|
70
|
+
def tool_call_schema(self) -> type[pydantic.BaseModel]:
|
71
|
+
"""
|
72
|
+
Defines the schema for tool calls based on the provided argument schema.
|
73
|
+
|
74
|
+
Returns:
|
75
|
+
type[pydantic.BaseModel]: The Pydantic model representing the tool call schema.
|
76
|
+
"""
|
77
|
+
assert self.args_schema is not None # noqa: S101
|
78
|
+
return self.args_schema
|
79
|
+
|
80
|
+
|
81
|
+
class ChainInput(pydantic.BaseModel):
|
82
|
+
"""
|
83
|
+
A Pydantic model representing the input structure for a chain.
|
84
|
+
"""
|
85
|
+
|
86
|
+
inputs: str
|
87
|
+
|
88
|
+
|
89
|
+
@dataclass
|
90
|
+
class ChainToolkit:
|
91
|
+
"""
|
92
|
+
A toolkit for managing and initializing a chain of agents.
|
93
|
+
"""
|
94
|
+
|
95
|
+
client: AuthenticatedClient
|
96
|
+
chain: list[AgentChain]
|
97
|
+
_cloud: bool = False
|
98
|
+
_service_name: str | None = None
|
99
|
+
_chain: list[Agent] | None = None
|
100
|
+
|
101
|
+
model_config = pydantic.ConfigDict(arbitrary_types_allowed=True)
|
102
|
+
|
103
|
+
def initialize(self) -> None:
|
104
|
+
"""
|
105
|
+
Initializes the toolkit by retrieving and configuring the list of agents based on the provided chains.
|
106
|
+
|
107
|
+
Raises:
|
108
|
+
RuntimeError: If initialization fails due to missing agents.
|
109
|
+
"""
|
110
|
+
"""Initialize the session and retrieve tools list"""
|
111
|
+
settings = get_settings()
|
112
|
+
self._cloud = settings.cloud
|
113
|
+
if self._chain is None:
|
114
|
+
agents = list_agents.sync_detailed(
|
115
|
+
client=self.client,
|
116
|
+
).parsed
|
117
|
+
chain_enabled = [chain for chain in self.chain if chain.enabled]
|
118
|
+
agents_chain = []
|
119
|
+
for chain in chain_enabled:
|
120
|
+
agent = [agent for agent in agents if agent.metadata.name == chain.name]
|
121
|
+
agent_name = agent[0].metadata.name.upper().replace("-", "_")
|
122
|
+
if os.getenv(f"BL_AGENT_{agent_name}_SERVICE_NAME"):
|
123
|
+
self._service_name = os.getenv(f"BL_AGENT_{agent_name}_SERVICE_NAME")
|
124
|
+
if agent:
|
125
|
+
agent[0].spec.prompt = chain.prompt or agent[0].spec.prompt
|
126
|
+
agent[0].spec.description = chain.description or agent[0].spec.description
|
127
|
+
agents_chain.append(agent[0])
|
128
|
+
self._chain = agents_chain
|
129
|
+
|
130
|
+
def get_tools(self) -> list[BaseTool]:
|
131
|
+
"""
|
132
|
+
Retrieves a list of tools corresponding to the initialized agents.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
list[BaseTool]: A list of initialized `ChainTool` instances.
|
136
|
+
|
137
|
+
Raises:
|
138
|
+
RuntimeError: If the toolkit has not been initialized.
|
139
|
+
"""
|
140
|
+
if self._chain is None:
|
141
|
+
raise RuntimeError("Must initialize the toolkit first")
|
142
|
+
|
143
|
+
return [
|
144
|
+
ChainTool(
|
145
|
+
client=RunClient(self.client),
|
146
|
+
name=agent.metadata.name,
|
147
|
+
description=agent.spec.description or agent.spec.prompt or "",
|
148
|
+
args_schema=ChainInput,
|
149
|
+
cloud=self._cloud,
|
150
|
+
service_name=self._service_name,
|
151
|
+
)
|
152
|
+
for agent in self._chain
|
153
|
+
]
|
blaxel/agents/chat.py
ADDED
@@ -0,0 +1,286 @@
|
|
1
|
+
from logging import getLogger
|
2
|
+
from typing import Tuple, Union
|
3
|
+
|
4
|
+
from langchain_core.language_models import BaseChatModel
|
5
|
+
|
6
|
+
from blaxel.aimon.settings import get_settings
|
7
|
+
from blaxel.api.models import get_model
|
8
|
+
from blaxel.authentication import get_authentication_headers, new_client
|
9
|
+
from blaxel.models import Model
|
10
|
+
|
11
|
+
from .voice.openai import OpenAIVoiceReactAgent
|
12
|
+
|
13
|
+
logger = getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def get_base_url(name: str) -> str:
|
17
|
+
"""
|
18
|
+
Constructs the base URL for a given model name based on the current settings.
|
19
|
+
|
20
|
+
Parameters:
|
21
|
+
name (str): The name of the model.
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
str: The constructed base URL.
|
25
|
+
"""
|
26
|
+
settings = get_settings()
|
27
|
+
return f"{settings.run_url}/{settings.workspace}/models/{name}/v1"
|
28
|
+
|
29
|
+
|
30
|
+
def get_mistral_chat_model(**kwargs) -> BaseChatModel:
|
31
|
+
"""
|
32
|
+
Initializes and returns a MistralAI chat model with the provided keyword arguments.
|
33
|
+
|
34
|
+
Parameters:
|
35
|
+
**kwargs: Arbitrary keyword arguments for configuring the `ChatMistralAI` model.
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
ChatMistralAI: An instance of the MistralAI chat model.
|
39
|
+
"""
|
40
|
+
from langchain_mistralai.chat_models import ChatMistralAI # type: ignore
|
41
|
+
|
42
|
+
return ChatMistralAI(**kwargs)
|
43
|
+
|
44
|
+
|
45
|
+
def get_openai_chat_model(**kwargs) -> BaseChatModel:
|
46
|
+
"""
|
47
|
+
Initializes and returns an OpenAI chat model with the provided keyword arguments.
|
48
|
+
|
49
|
+
Parameters:
|
50
|
+
**kwargs: Arbitrary keyword arguments for configuring the `ChatOpenAI` model.
|
51
|
+
|
52
|
+
Returns:
|
53
|
+
ChatOpenAI: An instance of the OpenAI chat model.
|
54
|
+
"""
|
55
|
+
from langchain_openai import ChatOpenAI # type: ignore
|
56
|
+
|
57
|
+
return ChatOpenAI(**kwargs)
|
58
|
+
|
59
|
+
|
60
|
+
def get_anthropic_chat_model(**kwargs) -> BaseChatModel:
|
61
|
+
"""
|
62
|
+
Initializes and returns an Anthropic chat model with the provided keyword arguments.
|
63
|
+
|
64
|
+
Parameters:
|
65
|
+
**kwargs: Arbitrary keyword arguments for configuring the `ChatAnthropic` model.
|
66
|
+
|
67
|
+
Returns:
|
68
|
+
ChatAnthropic: An instance of the Anthropic chat model.
|
69
|
+
"""
|
70
|
+
from langchain_anthropic import ChatAnthropic # type: ignore
|
71
|
+
|
72
|
+
return ChatAnthropic(**kwargs)
|
73
|
+
|
74
|
+
|
75
|
+
def get_xai_chat_model(**kwargs) -> BaseChatModel:
|
76
|
+
"""
|
77
|
+
Initializes and returns an XAI chat model with the provided keyword arguments.
|
78
|
+
|
79
|
+
Parameters:
|
80
|
+
**kwargs: Arbitrary keyword arguments for configuring the `ChatXAI` model.
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
ChatXAI: An instance of the XAI chat model.
|
84
|
+
"""
|
85
|
+
from langchain_xai import ChatXAI # type: ignore
|
86
|
+
|
87
|
+
return ChatXAI(**kwargs)
|
88
|
+
|
89
|
+
|
90
|
+
def get_cohere_chat_model(**kwargs) -> BaseChatModel:
|
91
|
+
"""
|
92
|
+
Initializes and returns a Cohere chat model with the provided keyword arguments.
|
93
|
+
|
94
|
+
Parameters:
|
95
|
+
**kwargs: Arbitrary keyword arguments for configuring the `ChatCohere` model.
|
96
|
+
|
97
|
+
Returns:
|
98
|
+
ChatCohere: An instance of the Cohere chat model.
|
99
|
+
"""
|
100
|
+
from langchain_cohere import ChatCohere # type: ignore
|
101
|
+
|
102
|
+
return ChatCohere(**kwargs)
|
103
|
+
|
104
|
+
def get_deepseek_chat_model(**kwargs):
|
105
|
+
from langchain_deepseek import ChatDeepSeek # type: ignore
|
106
|
+
|
107
|
+
return ChatDeepSeek(**kwargs)
|
108
|
+
|
109
|
+
def get_azure_ai_inference_chat_model(**kwargs):
|
110
|
+
from langchain_openai import ChatOpenAI # type: ignore
|
111
|
+
|
112
|
+
return ChatOpenAI(
|
113
|
+
**kwargs
|
114
|
+
) # It uses a compatible endpoint, so we can use the ChatOpenAI interface
|
115
|
+
|
116
|
+
def get_azure_marketplace_chat_model(**kwargs):
|
117
|
+
from langchain_openai import OpenAI # type: ignore
|
118
|
+
|
119
|
+
return OpenAI(
|
120
|
+
**kwargs
|
121
|
+
) # It seems to use a compatible endpoint, so we can use the classic OpenAI interface
|
122
|
+
|
123
|
+
def get_gemini_chat_model(**kwargs):
|
124
|
+
from langchain_google_genai import ChatGoogleGenerativeAI # type: ignore
|
125
|
+
|
126
|
+
return ChatGoogleGenerativeAI(
|
127
|
+
**kwargs,
|
128
|
+
)
|
129
|
+
|
130
|
+
def get_chat_model(name: str, agent_model: Union[Model, None] = None) -> BaseChatModel:
|
131
|
+
"""
|
132
|
+
Gets a chat model instance for the specified model name.
|
133
|
+
|
134
|
+
Parameters:
|
135
|
+
name (str): The name of the model to retrieve.
|
136
|
+
agent_model (Union[Model, None], optional): A pre-fetched model instance.
|
137
|
+
If None, the model will be fetched from the API. Defaults to None.
|
138
|
+
|
139
|
+
Returns:
|
140
|
+
BaseChatModel: An instance of the appropriate chat model.
|
141
|
+
"""
|
142
|
+
[chat_model, _, __] = get_chat_model_full(name, agent_model)
|
143
|
+
return chat_model
|
144
|
+
|
145
|
+
def get_chat_model_full(name: str, agent_model: Union[Model, None] = None) -> Tuple[BaseChatModel, str, str]:
|
146
|
+
"""
|
147
|
+
Gets a chat model instance along with provider and model information.
|
148
|
+
|
149
|
+
Parameters:
|
150
|
+
name (str): The name of the model to retrieve.
|
151
|
+
agent_model (Union[Model, None], optional): A pre-fetched model instance.
|
152
|
+
If None, the model will be fetched from the API. Defaults to None.
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
Tuple[BaseChatModel, str, str]: A tuple containing:
|
156
|
+
- The chat model instance
|
157
|
+
- The provider name (e.g., 'openai', 'anthropic', etc.)
|
158
|
+
- The specific model name (e.g., 'gpt-4o-mini')
|
159
|
+
"""
|
160
|
+
settings = get_settings()
|
161
|
+
client = new_client()
|
162
|
+
|
163
|
+
if agent_model is None:
|
164
|
+
try:
|
165
|
+
agent_model = get_model.sync(name, client=client)
|
166
|
+
except Exception:
|
167
|
+
logger.warning(f"Model {name} not found, defaulting to gpt-4o-mini")
|
168
|
+
|
169
|
+
headers = get_authentication_headers(settings)
|
170
|
+
|
171
|
+
jwt = headers.get("X-Blaxel-Authorization", "").replace("Bearer ", "")
|
172
|
+
chat_classes = {
|
173
|
+
"openai": {
|
174
|
+
"func": get_openai_chat_model,
|
175
|
+
"kwargs": {
|
176
|
+
"http_async_client": client.get_async_httpx_client(),
|
177
|
+
"http_client": client.get_httpx_client(),
|
178
|
+
},
|
179
|
+
},
|
180
|
+
"anthropic": {
|
181
|
+
"func": get_anthropic_chat_model,
|
182
|
+
"kwargs": {
|
183
|
+
"base_url": get_base_url(name).replace("/v1", ""),
|
184
|
+
},
|
185
|
+
"remove_kwargs": ["default_query"]
|
186
|
+
},
|
187
|
+
"mistral": {
|
188
|
+
"func": get_mistral_chat_model,
|
189
|
+
"kwargs": {
|
190
|
+
"api_key": jwt,
|
191
|
+
},
|
192
|
+
},
|
193
|
+
"xai": {
|
194
|
+
"func": get_xai_chat_model,
|
195
|
+
"kwargs": {
|
196
|
+
"api_key": jwt,
|
197
|
+
"xai_api_base": get_base_url(name),
|
198
|
+
},
|
199
|
+
"remove_kwargs": ["base_url"],
|
200
|
+
},
|
201
|
+
"cohere": {
|
202
|
+
"func": get_cohere_chat_model,
|
203
|
+
"kwargs": {
|
204
|
+
"cohere_api_key": jwt,
|
205
|
+
"base_url": get_base_url(name).replace("/v1", ""),
|
206
|
+
},
|
207
|
+
},
|
208
|
+
"deepseek": {
|
209
|
+
"func": get_deepseek_chat_model,
|
210
|
+
"kwargs": {
|
211
|
+
"api_key": jwt,
|
212
|
+
"api_base": get_base_url(name),
|
213
|
+
},
|
214
|
+
},
|
215
|
+
"azure-ai-inference": {
|
216
|
+
"func": get_azure_ai_inference_chat_model,
|
217
|
+
"kwargs": {
|
218
|
+
"base_url": get_base_url(name).replace("/v1", ""),
|
219
|
+
},
|
220
|
+
},
|
221
|
+
"azure-marketplace": {
|
222
|
+
"func": get_azure_marketplace_chat_model,
|
223
|
+
"kwargs": {},
|
224
|
+
},
|
225
|
+
"gemini": {
|
226
|
+
"func": get_gemini_chat_model,
|
227
|
+
"kwargs": {
|
228
|
+
"api_key": "fake_api_key",
|
229
|
+
"client_options": {
|
230
|
+
"api_endpoint": get_base_url(name).replace("/v1", "")
|
231
|
+
},
|
232
|
+
"transport": "rest",
|
233
|
+
"additional_headers": {"X-Blaxel-Authorization": f"Bearer {jwt}"},
|
234
|
+
},
|
235
|
+
"remove_kwargs": ["api_key", "default_headers"]
|
236
|
+
},
|
237
|
+
}
|
238
|
+
|
239
|
+
provider = (
|
240
|
+
agent_model
|
241
|
+
and agent_model.spec
|
242
|
+
and agent_model.spec.runtime
|
243
|
+
and agent_model.spec.runtime.type_
|
244
|
+
)
|
245
|
+
if not provider:
|
246
|
+
logger.warning("Provider not found in agent model, defaulting to OpenAI")
|
247
|
+
provider = "openai"
|
248
|
+
|
249
|
+
model = (
|
250
|
+
agent_model
|
251
|
+
and agent_model.spec
|
252
|
+
and agent_model.spec.runtime
|
253
|
+
and agent_model.spec.runtime.model
|
254
|
+
)
|
255
|
+
if not model:
|
256
|
+
logger.warning("Model not found in agent model, defaulting to gpt-4o-mini")
|
257
|
+
model = "gpt-4o-mini"
|
258
|
+
|
259
|
+
if provider == "openai" and "realtime" in model:
|
260
|
+
logger.info("Starting OpenAI Realtime Agent")
|
261
|
+
return (
|
262
|
+
OpenAIVoiceReactAgent(
|
263
|
+
url=get_base_url(name),
|
264
|
+
model=model,
|
265
|
+
headers=headers
|
266
|
+
),
|
267
|
+
provider,
|
268
|
+
model
|
269
|
+
)
|
270
|
+
kwargs = {
|
271
|
+
"model": model,
|
272
|
+
"base_url": get_base_url(name),
|
273
|
+
"default_headers": headers,
|
274
|
+
"api_key": "fake_api_key",
|
275
|
+
"temperature": 0,
|
276
|
+
}
|
277
|
+
chat_class = chat_classes.get(provider)
|
278
|
+
if not chat_class:
|
279
|
+
logger.warning(f"Provider {provider} not currently supported, defaulting to OpenAI")
|
280
|
+
chat_class = chat_classes["openai"]
|
281
|
+
if "kwargs" in chat_class:
|
282
|
+
kwargs.update(chat_class["kwargs"])
|
283
|
+
if "remove_kwargs" in chat_class:
|
284
|
+
for key in chat_class["remove_kwargs"]:
|
285
|
+
kwargs.pop(key, None)
|
286
|
+
return chat_class["func"](**kwargs), provider, model
|
@@ -0,0 +1,208 @@
|
|
1
|
+
"""Module: decorator
|
2
|
+
|
3
|
+
Defines decorators for agent functionalities.
|
4
|
+
"""
|
5
|
+
|
6
|
+
# Import necessary modules
|
7
|
+
import asyncio
|
8
|
+
import functools
|
9
|
+
import inspect
|
10
|
+
from logging import getLogger
|
11
|
+
from typing import Any, Callable
|
12
|
+
|
13
|
+
from langgraph.checkpoint.memory import MemorySaver
|
14
|
+
from langgraph.prebuilt import create_react_agent
|
15
|
+
|
16
|
+
from blaxel.aimon.settings import Settings, init
|
17
|
+
from blaxel.api.models import get_model, list_models
|
18
|
+
from blaxel.authentication import new_client
|
19
|
+
from blaxel.errors import UnexpectedStatus
|
20
|
+
from blaxel.functions import get_functions
|
21
|
+
from blaxel.models import Agent, AgentSpec, Metadata
|
22
|
+
|
23
|
+
from .chat import get_chat_model_full
|
24
|
+
from .voice.openai import OpenAIVoiceReactAgent
|
25
|
+
|
26
|
+
|
27
|
+
async def initialize_agent(
|
28
|
+
settings: Settings,
|
29
|
+
agent: Agent | dict = None,
|
30
|
+
override_model=None,
|
31
|
+
override_agent=None,
|
32
|
+
override_functions=None,
|
33
|
+
remote_functions=None,
|
34
|
+
local_functions=None,
|
35
|
+
):
|
36
|
+
logger = getLogger(__name__)
|
37
|
+
client = new_client()
|
38
|
+
chat_model = override_model or None
|
39
|
+
|
40
|
+
if asyncio.iscoroutinefunction(override_agent):
|
41
|
+
override_agent = await override_agent()
|
42
|
+
|
43
|
+
if agent is not None:
|
44
|
+
metadata = Metadata(**agent.get("metadata", {}))
|
45
|
+
spec = AgentSpec(**agent.get("spec", {}))
|
46
|
+
agent = Agent(metadata=metadata, spec=spec)
|
47
|
+
if agent.spec.model and chat_model is None:
|
48
|
+
try:
|
49
|
+
response = get_model.sync_detailed(
|
50
|
+
agent.spec.model, client=client
|
51
|
+
)
|
52
|
+
settings.agent.model = response.parsed
|
53
|
+
except UnexpectedStatus as e:
|
54
|
+
if e.status_code == 404:
|
55
|
+
if e.status_code == 404:
|
56
|
+
raise ValueError(f"Model {agent.spec.model} not found")
|
57
|
+
raise e
|
58
|
+
except Exception as e:
|
59
|
+
raise e
|
60
|
+
|
61
|
+
if settings.agent.model:
|
62
|
+
chat_model, provider, model = get_chat_model_full(agent.spec.model, settings.agent.model)
|
63
|
+
settings.agent.chat_model = chat_model
|
64
|
+
logger.info(f"Chat model configured, using: {provider}:{model}")
|
65
|
+
|
66
|
+
if override_functions is not None:
|
67
|
+
functions = override_functions
|
68
|
+
else:
|
69
|
+
functions = await get_functions(
|
70
|
+
client=client,
|
71
|
+
dir=settings.agent.functions_directory,
|
72
|
+
remote_functions=remote_functions,
|
73
|
+
chain=agent.spec.agent_chain,
|
74
|
+
local_functions=local_functions,
|
75
|
+
remote_functions_empty=not remote_functions,
|
76
|
+
warning=chat_model is not None,
|
77
|
+
)
|
78
|
+
settings.agent.functions = functions
|
79
|
+
|
80
|
+
if override_agent is None:
|
81
|
+
if chat_model is None:
|
82
|
+
models_select = ""
|
83
|
+
try:
|
84
|
+
models = list_models.sync_detailed(
|
85
|
+
client=client
|
86
|
+
)
|
87
|
+
models = ", ".join([model.metadata.name for model in models.parsed])
|
88
|
+
models_select = f"You can select one from your models: {models}"
|
89
|
+
except Exception:
|
90
|
+
pass
|
91
|
+
|
92
|
+
raise ValueError(
|
93
|
+
f"You must provide a model.\n"
|
94
|
+
f"{models_select}\n"
|
95
|
+
f"You can create one at {settings.app_url}/{settings.workspace}/global-inference-network/models/create\n"
|
96
|
+
"Add it to your agent spec\n"
|
97
|
+
"agent={\n"
|
98
|
+
' "metadata": {\n'
|
99
|
+
f' "name": "{agent.metadata.name}",\n'
|
100
|
+
" },\n"
|
101
|
+
' "spec": {\n'
|
102
|
+
' "model": "MODEL_NAME",\n'
|
103
|
+
f' "description": "{agent.spec.description}",\n'
|
104
|
+
f' "prompt": "{agent.spec.prompt}",\n'
|
105
|
+
" },\n"
|
106
|
+
"}")
|
107
|
+
if isinstance(chat_model, OpenAIVoiceReactAgent):
|
108
|
+
_agent = chat_model
|
109
|
+
else:
|
110
|
+
memory = MemorySaver()
|
111
|
+
if len(functions) == 0:
|
112
|
+
raise ValueError("You can define this function in directory "
|
113
|
+
f'"{settings.agent.functions_directory}". Here is a sample function you can use:\n\n'
|
114
|
+
"from blaxel.functions import function\n\n"
|
115
|
+
"@function()\n"
|
116
|
+
"def hello_world(query: str):\n"
|
117
|
+
" return 'Hello, world!'\n")
|
118
|
+
try:
|
119
|
+
_agent = create_react_agent(chat_model, functions, checkpointer=memory, state_modifier=agent.spec.prompt or "")
|
120
|
+
except AttributeError: # special case for azure-marketplace where it uses the old OpenAI interface (no tools)
|
121
|
+
logger.warning("Using the old OpenAI interface for Azure Marketplace, no tools available")
|
122
|
+
_agent = create_react_agent(chat_model, [], checkpointer=memory, state_modifier=(agent and agent.spec and agent.spec.prompt) or "")
|
123
|
+
|
124
|
+
settings.agent.agent = _agent
|
125
|
+
else:
|
126
|
+
settings.agent.agent = override_agent
|
127
|
+
|
128
|
+
def agent(
|
129
|
+
agent: Agent | dict = None,
|
130
|
+
override_model=None,
|
131
|
+
override_agent: Any | Callable = None,
|
132
|
+
override_functions=None,
|
133
|
+
remote_functions=None,
|
134
|
+
local_functions=None,
|
135
|
+
) -> Callable:
|
136
|
+
"""
|
137
|
+
A decorator factory that configures and wraps functions to integrate with Blaxel agents.
|
138
|
+
Handles model initialization, function retrieval, and agent setup.
|
139
|
+
|
140
|
+
Parameters:
|
141
|
+
agent (Agent | dict, optional): An `Agent` instance or a dictionary containing agent metadata and specifications.
|
142
|
+
override_model (Any, optional): An optional model to override the default agent model.
|
143
|
+
override_agent (Any, Callable, optional): An optional agent instance to override the default agent, or a function which returns an agent instance.
|
144
|
+
mcp_hub (Any, optional): An optional MCP hub configuration.
|
145
|
+
remote_functions (Any, optional): An optional list of remote functions to be integrated.
|
146
|
+
local_functions (Any, optional): An optional list of local functions to be integrated.
|
147
|
+
|
148
|
+
Returns:
|
149
|
+
Callable: A decorator that wraps the target function, injecting agent-related configurations and dependencies.
|
150
|
+
|
151
|
+
Behavior:
|
152
|
+
- Validates and initializes the agent configuration.
|
153
|
+
- Retrieves and sets up the appropriate chat model based on the agent's specifications.
|
154
|
+
- Retrieves functions from the specified directories or remote sources.
|
155
|
+
- Wraps the target function, injecting agent, model, and function dependencies as needed.
|
156
|
+
- Logs relevant information and handles exceptions during the setup process.
|
157
|
+
|
158
|
+
Raises:
|
159
|
+
ValueError: If required configurations such as the model are missing.
|
160
|
+
Re-raises exceptions encountered during model retrieval and agent setup.
|
161
|
+
"""
|
162
|
+
logger = getLogger(__name__)
|
163
|
+
settings = init()
|
164
|
+
_is_initialized = False
|
165
|
+
try:
|
166
|
+
if agent is not None and not isinstance(agent, dict):
|
167
|
+
raise Exception(
|
168
|
+
'agent must be a dictionary, example: @agent(agent={"metadata": {"name": "my_agent"}})'
|
169
|
+
)
|
170
|
+
|
171
|
+
|
172
|
+
def wrapper(func):
|
173
|
+
|
174
|
+
agent_kwargs = any(
|
175
|
+
param.name == "agent"
|
176
|
+
for param in inspect.signature(func).parameters.values()
|
177
|
+
)
|
178
|
+
model_kwargs = any(
|
179
|
+
param.name == "model"
|
180
|
+
for param in inspect.signature(func).parameters.values()
|
181
|
+
)
|
182
|
+
functions_kwargs = any(
|
183
|
+
param.name == "functions"
|
184
|
+
for param in inspect.signature(func).parameters.values()
|
185
|
+
)
|
186
|
+
|
187
|
+
@functools.wraps(func)
|
188
|
+
async def wrapped(*args, **kwargs):
|
189
|
+
nonlocal _is_initialized
|
190
|
+
if not _is_initialized:
|
191
|
+
async with asyncio.Lock():
|
192
|
+
if not _is_initialized:
|
193
|
+
await initialize_agent(settings, agent, override_model, override_agent, override_functions, remote_functions, local_functions)
|
194
|
+
_is_initialized = True
|
195
|
+
if agent_kwargs:
|
196
|
+
kwargs["agent"] = settings.agent.agent
|
197
|
+
if model_kwargs:
|
198
|
+
kwargs["model"] = settings.agent.chat_model
|
199
|
+
if functions_kwargs:
|
200
|
+
kwargs["functions"] = settings.agent.functions
|
201
|
+
return await func(*args, **kwargs)
|
202
|
+
|
203
|
+
return wrapped
|
204
|
+
|
205
|
+
return wrapper
|
206
|
+
except Exception as e:
|
207
|
+
logger.error(f"Error in agent decorator: {e!s} at line {e.__traceback__.tb_lineno}")
|
208
|
+
raise e
|
blaxel/agents/thread.py
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
"""Module: thread
|
2
|
+
|
3
|
+
Defines threading capabilities for agents.
|
4
|
+
"""
|
5
|
+
from fastapi import Request
|
6
|
+
|
7
|
+
|
8
|
+
def get_default_thread(request: Request) -> str:
|
9
|
+
"""
|
10
|
+
Extracts the default thread identifier from an incoming HTTP request.
|
11
|
+
Prioritizes the `X-Blaxel-Sub` header and falls back to decoding the JWT
|
12
|
+
from the `Authorization` or `X-Blaxel-Authorization` headers.
|
13
|
+
|
14
|
+
Parameters:
|
15
|
+
request (Request): The incoming HTTP request object.
|
16
|
+
|
17
|
+
Returns:
|
18
|
+
str: The extracted thread identifier. Returns an empty string if no valid identifier is found.
|
19
|
+
"""
|
20
|
+
if request.headers.get("X-Blaxel-Thread-Id"):
|
21
|
+
return request.headers.get("X-Blaxel-Thread-Id")
|
22
|
+
if request.headers.get("Thread-Id"):
|
23
|
+
return request.headers.get("Thread-Id")
|
24
|
+
return ""
|