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.
Files changed (261) hide show
  1. blaxel/__init__.py +8 -0
  2. blaxel/agents/__init__.py +5 -0
  3. blaxel/agents/chain.py +153 -0
  4. blaxel/agents/chat.py +286 -0
  5. blaxel/agents/decorator.py +208 -0
  6. blaxel/agents/thread.py +24 -0
  7. blaxel/agents/voice/openai.py +255 -0
  8. blaxel/agents/voice/utils.py +25 -0
  9. blaxel/api/__init__.py +1 -0
  10. blaxel/api/agents/__init__.py +0 -0
  11. blaxel/api/agents/create_agent.py +155 -0
  12. blaxel/api/agents/delete_agent.py +146 -0
  13. blaxel/api/agents/get_agent.py +146 -0
  14. blaxel/api/agents/get_agent_logs.py +151 -0
  15. blaxel/api/agents/get_agent_metrics.py +150 -0
  16. blaxel/api/agents/get_agent_trace_ids.py +201 -0
  17. blaxel/api/agents/list_agent_revisions.py +155 -0
  18. blaxel/api/agents/list_agents.py +127 -0
  19. blaxel/api/agents/update_agent.py +168 -0
  20. blaxel/api/configurations/__init__.py +0 -0
  21. blaxel/api/configurations/get_configuration.py +122 -0
  22. blaxel/api/default/__init__.py +0 -0
  23. blaxel/api/default/get_trace.py +150 -0
  24. blaxel/api/default/get_trace_ids.py +218 -0
  25. blaxel/api/default/get_trace_logs.py +186 -0
  26. blaxel/api/default/list_mcp_hub_definitions.py +127 -0
  27. blaxel/api/functions/__init__.py +0 -0
  28. blaxel/api/functions/create_function.py +155 -0
  29. blaxel/api/functions/delete_function.py +146 -0
  30. blaxel/api/functions/get_function.py +146 -0
  31. blaxel/api/functions/get_function_logs.py +151 -0
  32. blaxel/api/functions/get_function_metrics.py +150 -0
  33. blaxel/api/functions/get_function_trace_ids.py +201 -0
  34. blaxel/api/functions/list_function_revisions.py +158 -0
  35. blaxel/api/functions/list_functions.py +131 -0
  36. blaxel/api/functions/update_function.py +168 -0
  37. blaxel/api/integrations/__init__.py +0 -0
  38. blaxel/api/integrations/create_integration_connection.py +167 -0
  39. blaxel/api/integrations/delete_integration_connection.py +158 -0
  40. blaxel/api/integrations/get_integration.py +97 -0
  41. blaxel/api/integrations/get_integration_connection.py +158 -0
  42. blaxel/api/integrations/get_integration_connection_model.py +104 -0
  43. blaxel/api/integrations/get_integration_connection_model_endpoint_configurations.py +97 -0
  44. blaxel/api/integrations/list_integration_connection_models.py +97 -0
  45. blaxel/api/integrations/list_integration_connections.py +139 -0
  46. blaxel/api/integrations/update_integration_connection.py +180 -0
  47. blaxel/api/invitations/__init__.py +0 -0
  48. blaxel/api/invitations/list_all_pending_invitations.py +142 -0
  49. blaxel/api/knowledgebases/__init__.py +0 -0
  50. blaxel/api/knowledgebases/create_knowledgebase.py +163 -0
  51. blaxel/api/knowledgebases/delete_knowledgebase.py +154 -0
  52. blaxel/api/knowledgebases/get_knowledgebase.py +154 -0
  53. blaxel/api/knowledgebases/list_knowledgebase_revisions.py +158 -0
  54. blaxel/api/knowledgebases/list_knowledgebases.py +139 -0
  55. blaxel/api/knowledgebases/update_knowledgebase.py +176 -0
  56. blaxel/api/locations/__init__.py +0 -0
  57. blaxel/api/locations/list_locations.py +139 -0
  58. blaxel/api/metrics/__init__.py +0 -0
  59. blaxel/api/metrics/get_metrics.py +130 -0
  60. blaxel/api/models/__init__.py +0 -0
  61. blaxel/api/models/create_model.py +163 -0
  62. blaxel/api/models/delete_model.py +154 -0
  63. blaxel/api/models/get_model.py +154 -0
  64. blaxel/api/models/get_model_logs.py +155 -0
  65. blaxel/api/models/get_model_metrics.py +158 -0
  66. blaxel/api/models/get_model_trace_ids.py +201 -0
  67. blaxel/api/models/list_model_revisions.py +158 -0
  68. blaxel/api/models/list_models.py +135 -0
  69. blaxel/api/models/update_model.py +176 -0
  70. blaxel/api/policies/__init__.py +0 -0
  71. blaxel/api/policies/create_policy.py +167 -0
  72. blaxel/api/policies/delete_policy.py +154 -0
  73. blaxel/api/policies/get_policy.py +154 -0
  74. blaxel/api/policies/list_policies.py +139 -0
  75. blaxel/api/policies/update_policy.py +180 -0
  76. blaxel/api/privateclusters/__init__.py +0 -0
  77. blaxel/api/privateclusters/create_private_cluster.py +132 -0
  78. blaxel/api/privateclusters/delete_private_cluster.py +156 -0
  79. blaxel/api/privateclusters/get_private_cluster.py +159 -0
  80. blaxel/api/privateclusters/get_private_cluster_health.py +97 -0
  81. blaxel/api/privateclusters/list_private_clusters.py +140 -0
  82. blaxel/api/privateclusters/update_private_cluster.py +156 -0
  83. blaxel/api/privateclusters/update_private_cluster_health.py +97 -0
  84. blaxel/api/service_accounts/__init__.py +0 -0
  85. blaxel/api/service_accounts/create_api_key_for_service_account.py +177 -0
  86. blaxel/api/service_accounts/create_workspace_service_account.py +170 -0
  87. blaxel/api/service_accounts/delete_api_key_for_service_account.py +104 -0
  88. blaxel/api/service_accounts/delete_workspace_service_account.py +160 -0
  89. blaxel/api/service_accounts/get_workspace_service_accounts.py +141 -0
  90. blaxel/api/service_accounts/list_api_keys_for_service_account.py +163 -0
  91. blaxel/api/service_accounts/update_workspace_service_account.py +183 -0
  92. blaxel/api/store/__init__.py +0 -0
  93. blaxel/api/store/get_store_agent.py +146 -0
  94. blaxel/api/store/get_store_function.py +146 -0
  95. blaxel/api/store/list_store_agents.py +131 -0
  96. blaxel/api/store/list_store_functions.py +131 -0
  97. blaxel/api/workspaces/__init__.py +0 -0
  98. blaxel/api/workspaces/accept_workspace_invitation.py +161 -0
  99. blaxel/api/workspaces/create_worspace.py +163 -0
  100. blaxel/api/workspaces/decline_workspace_invitation.py +158 -0
  101. blaxel/api/workspaces/delete_workspace.py +154 -0
  102. blaxel/api/workspaces/get_workspace.py +154 -0
  103. blaxel/api/workspaces/invite_workspace_user.py +174 -0
  104. blaxel/api/workspaces/leave_workspace.py +161 -0
  105. blaxel/api/workspaces/list_workspace_users.py +139 -0
  106. blaxel/api/workspaces/list_workspaces.py +139 -0
  107. blaxel/api/workspaces/remove_workspace_user.py +101 -0
  108. blaxel/api/workspaces/update_workspace.py +176 -0
  109. blaxel/api/workspaces/update_workspace_user_role.py +187 -0
  110. blaxel/authentication/__init__.py +45 -0
  111. blaxel/authentication/apikey.py +50 -0
  112. blaxel/authentication/authentication.py +176 -0
  113. blaxel/authentication/clientcredentials.py +103 -0
  114. blaxel/authentication/credentials.py +295 -0
  115. blaxel/authentication/device_mode.py +197 -0
  116. blaxel/client.py +281 -0
  117. blaxel/common/__init__.py +17 -0
  118. blaxel/common/error.py +27 -0
  119. blaxel/common/instrumentation.py +317 -0
  120. blaxel/common/logger.py +60 -0
  121. blaxel/common/secrets.py +39 -0
  122. blaxel/common/settings.py +150 -0
  123. blaxel/common/slugify.py +18 -0
  124. blaxel/common/utils.py +34 -0
  125. blaxel/deploy/__init__.py +8 -0
  126. blaxel/deploy/deploy.py +316 -0
  127. blaxel/deploy/format.py +46 -0
  128. blaxel/deploy/parser.py +192 -0
  129. blaxel/errors.py +16 -0
  130. blaxel/functions/__init__.py +7 -0
  131. blaxel/functions/common.py +228 -0
  132. blaxel/functions/decorator.py +64 -0
  133. blaxel/functions/local/local.py +48 -0
  134. blaxel/functions/mcp/client.py +96 -0
  135. blaxel/functions/mcp/mcp.py +168 -0
  136. blaxel/functions/mcp/utils.py +56 -0
  137. blaxel/functions/remote/remote.py +183 -0
  138. blaxel/models/__init__.py +233 -0
  139. blaxel/models/acl.py +133 -0
  140. blaxel/models/agent.py +126 -0
  141. blaxel/models/agent_chain.py +88 -0
  142. blaxel/models/agent_spec.py +346 -0
  143. blaxel/models/api_key.py +142 -0
  144. blaxel/models/configuration.py +85 -0
  145. blaxel/models/continent.py +70 -0
  146. blaxel/models/core_event.py +97 -0
  147. blaxel/models/core_spec.py +249 -0
  148. blaxel/models/core_spec_configurations.py +77 -0
  149. blaxel/models/country.py +70 -0
  150. blaxel/models/create_api_key_for_service_account_body.py +69 -0
  151. blaxel/models/create_workspace_service_account_body.py +71 -0
  152. blaxel/models/create_workspace_service_account_response_200.py +105 -0
  153. blaxel/models/delete_workspace_service_account_response_200.py +96 -0
  154. blaxel/models/entrypoint.py +96 -0
  155. blaxel/models/entrypoint_env.py +45 -0
  156. blaxel/models/flavor.py +70 -0
  157. blaxel/models/form.py +120 -0
  158. blaxel/models/form_config.py +45 -0
  159. blaxel/models/form_oauthomitempty.py +45 -0
  160. blaxel/models/form_secrets.py +45 -0
  161. blaxel/models/function.py +126 -0
  162. blaxel/models/function_kit.py +97 -0
  163. blaxel/models/function_spec.py +310 -0
  164. blaxel/models/get_trace_ids_response_200.py +45 -0
  165. blaxel/models/get_trace_logs_response_200.py +45 -0
  166. blaxel/models/get_trace_response_200.py +45 -0
  167. blaxel/models/get_workspace_service_accounts_response_200_item.py +96 -0
  168. blaxel/models/histogram_bucket.py +79 -0
  169. blaxel/models/histogram_stats.py +88 -0
  170. blaxel/models/integration_connection.py +96 -0
  171. blaxel/models/integration_connection_spec.py +114 -0
  172. blaxel/models/integration_connection_spec_config.py +45 -0
  173. blaxel/models/integration_connection_spec_secret.py +45 -0
  174. blaxel/models/integration_model.py +162 -0
  175. blaxel/models/integration_repository.py +88 -0
  176. blaxel/models/invite_workspace_user_body.py +60 -0
  177. blaxel/models/knowledgebase.py +126 -0
  178. blaxel/models/knowledgebase_spec.py +163 -0
  179. blaxel/models/knowledgebase_spec_options.py +45 -0
  180. blaxel/models/last_n_requests_metric.py +79 -0
  181. blaxel/models/latency_metric.py +144 -0
  182. blaxel/models/location_response.py +113 -0
  183. blaxel/models/mcp_definition.py +188 -0
  184. blaxel/models/mcp_definition_entrypoint.py +45 -0
  185. blaxel/models/mcp_definition_form.py +45 -0
  186. blaxel/models/metadata.py +139 -0
  187. blaxel/models/metadata_labels.py +45 -0
  188. blaxel/models/metric.py +79 -0
  189. blaxel/models/metrics.py +169 -0
  190. blaxel/models/metrics_models.py +45 -0
  191. blaxel/models/metrics_request_total_per_code.py +45 -0
  192. blaxel/models/metrics_rps_per_code.py +45 -0
  193. blaxel/models/model.py +126 -0
  194. blaxel/models/model_private_cluster.py +79 -0
  195. blaxel/models/model_spec.py +249 -0
  196. blaxel/models/o_auth.py +72 -0
  197. blaxel/models/owner_fields.py +70 -0
  198. blaxel/models/pending_invitation.py +124 -0
  199. blaxel/models/pending_invitation_accept.py +85 -0
  200. blaxel/models/pending_invitation_render.py +147 -0
  201. blaxel/models/pending_invitation_render_invited_by.py +88 -0
  202. blaxel/models/pending_invitation_render_workspace.py +70 -0
  203. blaxel/models/pending_invitation_workspace_details.py +72 -0
  204. blaxel/models/pod_template_spec.py +45 -0
  205. blaxel/models/policy.py +96 -0
  206. blaxel/models/policy_location.py +70 -0
  207. blaxel/models/policy_max_tokens.py +106 -0
  208. blaxel/models/policy_spec.py +151 -0
  209. blaxel/models/private_cluster.py +183 -0
  210. blaxel/models/private_location.py +61 -0
  211. blaxel/models/repository.py +70 -0
  212. blaxel/models/request_duration_over_time_metric.py +97 -0
  213. blaxel/models/request_duration_over_time_metrics.py +80 -0
  214. blaxel/models/request_total_by_origin_metric.py +115 -0
  215. blaxel/models/request_total_by_origin_metric_request_total_by_origin.py +45 -0
  216. blaxel/models/request_total_by_origin_metric_request_total_by_origin_and_code.py +45 -0
  217. blaxel/models/request_total_metric.py +123 -0
  218. blaxel/models/request_total_metric_request_total_per_code.py +45 -0
  219. blaxel/models/request_total_metric_rps_per_code.py +45 -0
  220. blaxel/models/resource_log.py +79 -0
  221. blaxel/models/resource_metrics.py +270 -0
  222. blaxel/models/resource_metrics_request_total_per_code.py +45 -0
  223. blaxel/models/resource_metrics_rps_per_code.py +45 -0
  224. blaxel/models/revision_configuration.py +97 -0
  225. blaxel/models/revision_metadata.py +124 -0
  226. blaxel/models/runtime.py +196 -0
  227. blaxel/models/runtime_startup_probe.py +45 -0
  228. blaxel/models/serverless_config.py +80 -0
  229. blaxel/models/spec_configuration.py +70 -0
  230. blaxel/models/store_agent.py +178 -0
  231. blaxel/models/store_agent_labels.py +45 -0
  232. blaxel/models/store_configuration.py +151 -0
  233. blaxel/models/store_configuration_option.py +79 -0
  234. blaxel/models/store_function.py +211 -0
  235. blaxel/models/store_function_kit.py +97 -0
  236. blaxel/models/store_function_labels.py +45 -0
  237. blaxel/models/store_function_parameter.py +88 -0
  238. blaxel/models/time_fields.py +70 -0
  239. blaxel/models/token_rate_metric.py +88 -0
  240. blaxel/models/token_rate_metrics.py +120 -0
  241. blaxel/models/token_total_metric.py +106 -0
  242. blaxel/models/trace_ids_response.py +45 -0
  243. blaxel/models/update_workspace_service_account_body.py +69 -0
  244. blaxel/models/update_workspace_service_account_response_200.py +96 -0
  245. blaxel/models/update_workspace_user_role_body.py +60 -0
  246. blaxel/models/websocket_channel.py +88 -0
  247. blaxel/models/workspace.py +148 -0
  248. blaxel/models/workspace_labels.py +45 -0
  249. blaxel/models/workspace_user.py +115 -0
  250. blaxel/py.typed +1 -0
  251. blaxel/run.py +108 -0
  252. blaxel/serve/app.py +131 -0
  253. blaxel/serve/middlewares/__init__.py +10 -0
  254. blaxel/serve/middlewares/accesslog.py +32 -0
  255. blaxel/serve/middlewares/processtime.py +28 -0
  256. blaxel/types.py +46 -0
  257. blaxel-0.64.0.dist-info/METADATA +96 -0
  258. blaxel-0.64.0.dist-info/RECORD +261 -0
  259. blaxel-0.64.0.dist-info/WHEEL +4 -0
  260. blaxel-0.64.0.dist-info/entry_points.txt +2 -0
  261. blaxel-0.64.0.dist-info/licenses/LICENSE +21 -0
blaxel/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """A client library for accessing Blaxel Control Plane"""
2
+
3
+ from .client import AuthenticatedClient, Client
4
+
5
+ __all__ = (
6
+ "AuthenticatedClient",
7
+ "Client",
8
+ )
@@ -0,0 +1,5 @@
1
+ from .chat import get_chat_model, get_chat_model_full
2
+ from .decorator import agent
3
+ from .thread import get_default_thread
4
+
5
+ __all__ = ["agent", "get_chat_model", "get_chat_model_full", "get_default_thread"]
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
@@ -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 ""