blaxel 0.2.38rc122__py3-none-any.whl → 0.2.39__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 CHANGED
@@ -4,8 +4,8 @@ from .core.common.autoload import autoload
4
4
  from .core.common.env import env
5
5
  from .core.common.settings import settings
6
6
 
7
- __version__ = "0.2.38.pre-122"
8
- __commit__ = "e7e19d2c82b2b0c2a44914245734597adcc00f49"
7
+ __version__ = "0.2.39"
8
+ __commit__ = "48d4000d429ce3ce1d6d5b4858cf15115b380f96"
9
9
  __sentry_dsn__ = "https://9711de13cd02b285ca4378c01de8dc30@o4508714045276160.ingest.us.sentry.io/4510461121462272"
10
10
  __all__ = ["autoload", "settings", "env"]
11
11
 
@@ -115,6 +115,5 @@ class ClientCredentials(BlaxelAuth):
115
115
 
116
116
  @property
117
117
  def token(self):
118
- if not self.credentials.access_token:
119
- self.get_token()
118
+ self.get_token()
120
119
  return self.credentials.access_token
@@ -254,6 +254,10 @@ class PersistentMcpClient:
254
254
  await self.client_exit_stack.aclose()
255
255
  except Exception as e:
256
256
  logger.debug(f"Error closing client exit stack: {e}")
257
+ # Create fresh exit stacks so that future initialize() calls
258
+ # don't reuse stacks tainted by old cancel scopes
259
+ self.session_exit_stack = AsyncExitStack()
260
+ self.client_exit_stack = AsyncExitStack()
257
261
  logger.debug("WebSocket connection closed due to inactivity.")
258
262
 
259
263
 
@@ -137,6 +137,8 @@ class VolumeCreateConfiguration:
137
137
 
138
138
 
139
139
  class VolumeInstance:
140
+ delete: "_AsyncDeleteDescriptor"
141
+
140
142
  def __init__(self, volume: Volume):
141
143
  self.volume = volume
142
144
 
@@ -275,6 +277,8 @@ class VolumeInstance:
275
277
 
276
278
 
277
279
  class SyncVolumeInstance:
280
+ delete: "_SyncDeleteDescriptor"
281
+
278
282
  """Synchronous volume instance for managing persistent storage."""
279
283
 
280
284
  def __init__(self, volume: Volume):
blaxel/crewai/model.py CHANGED
@@ -1,6 +1,8 @@
1
1
  from logging import getLogger
2
2
 
3
- from crewai import LLM
3
+ import httpx
4
+ from crewai import LLM # type: ignore[import-not-found]
5
+ from crewai.llms.hooks.base import BaseInterceptor # type: ignore[import-not-found]
4
6
 
5
7
  from blaxel.core import bl_model as bl_model_core
6
8
  from blaxel.core import settings
@@ -8,62 +10,97 @@ from blaxel.core import settings
8
10
  logger = getLogger(__name__)
9
11
 
10
12
 
11
- class AuthenticatedLLM(LLM):
12
- def call(self, *args, **kwargs):
13
- self.additional_params["extra_headers"] = settings.auth.get_headers()
14
- return super().call(*args, **kwargs)
13
+ class AuthInterceptor(BaseInterceptor[httpx.Request, httpx.Response]):
14
+ """Interceptor that injects dynamic auth headers into every HTTP request.
15
+
16
+ Used for crewai native providers (OpenAI, Anthropic, Gemini, etc.)
17
+ where the LLM.__new__ factory returns a provider-specific instance
18
+ and subclass overrides are not possible.
19
+ """
20
+
21
+ def on_outbound(self, message: httpx.Request) -> httpx.Request:
22
+ auth_headers = settings.auth.get_headers()
23
+ # Remove the SDK's default "Authorization: Bearer replaced" header
24
+ # when our auth uses a different header (e.g. X-Blaxel-Authorization with API keys)
25
+ if "Authorization" not in auth_headers:
26
+ message.headers.pop("Authorization", None)
27
+ message.headers.pop("authorization", None)
28
+ for key, value in auth_headers.items():
29
+ message.headers[key] = value
30
+ return message
31
+
32
+ def on_inbound(self, message: httpx.Response) -> httpx.Response:
33
+ return message
34
+
35
+ async def aon_outbound(self, message: httpx.Request) -> httpx.Request:
36
+ return self.on_outbound(message)
37
+
38
+ async def aon_inbound(self, message: httpx.Response) -> httpx.Response:
39
+ return message
40
+
41
+
42
+ # Provider types that crewai routes to native SDK implementations.
43
+ # These support the interceptor mechanism for auth.
44
+ _NATIVE_PROVIDER_PREFIXES = {"openai", "anthropic", "gemini", "azure", "bedrock"}
45
+
46
+
47
+ def _is_native_route(provider_prefix: str) -> bool:
48
+ """Check if a provider prefix will be routed to a native SDK by crewai."""
49
+ return provider_prefix.lower() in _NATIVE_PROVIDER_PREFIXES
15
50
 
16
51
 
17
52
  async def bl_model(name: str, **kwargs):
18
53
  url, type, model = await bl_model_core(name).get_parameters()
54
+
55
+ # Map blaxel model types to crewai provider prefixes and base URLs
19
56
  if type == "mistral":
20
- return AuthenticatedLLM(
21
- model=f"mistral/{model}",
22
- api_key="replaced",
23
- base_url=f"{url}/v1",
24
- **kwargs,
25
- )
57
+ provider_prefix = "mistral"
58
+ base_url = f"{url}/v1"
26
59
  elif type == "xai":
27
- return AuthenticatedLLM(
28
- model=f"groq/{model}",
29
- api_key="replaced",
30
- base_url=f"{url}/v1",
31
- **kwargs,
32
- )
60
+ provider_prefix = "groq"
61
+ base_url = f"{url}/v1"
33
62
  elif type == "deepseek":
34
- return AuthenticatedLLM(
35
- model=f"openai/{model}",
36
- api_key="replaced",
37
- base_url=f"{url}/v1",
38
- **kwargs,
39
- )
63
+ provider_prefix = "openai"
64
+ base_url = f"{url}/v1"
40
65
  elif type == "anthropic":
41
- return AuthenticatedLLM(
42
- model=f"anthropic/{model}",
43
- api_key="replaced",
44
- base_url=url,
45
- **kwargs,
46
- )
66
+ provider_prefix = "anthropic"
67
+ base_url = url
47
68
  elif type == "gemini":
48
- return AuthenticatedLLM(
49
- model=f"gemini/{model}",
50
- api_key="replaced",
51
- base_url=f"{url}/v1beta/models/{model}",
52
- **kwargs,
53
- )
69
+ provider_prefix = "gemini"
70
+ base_url = f"{url}/v1beta/models/{model}"
54
71
  elif type == "cerebras":
55
- return AuthenticatedLLM(
56
- model=f"cerebras/{model}",
57
- api_key="replaced",
58
- base_url=f"{url}/v1",
59
- **kwargs,
60
- )
72
+ provider_prefix = "cerebras"
73
+ base_url = f"{url}/v1"
61
74
  else:
62
75
  if type != "openai":
63
76
  logger.warning(f"Model {model} is not supported by CrewAI, defaulting to OpenAI")
64
- return AuthenticatedLLM(
65
- model=f"openai/{model}",
77
+ provider_prefix = "openai"
78
+ base_url = f"{url}/v1"
79
+
80
+ model_string = f"{provider_prefix}/{model}"
81
+ auth_headers = settings.auth.get_headers()
82
+
83
+ if _is_native_route(provider_prefix):
84
+ # Native providers: use interceptor for dynamic auth headers.
85
+ # Always pass api_key="replaced" because crewai's native providers
86
+ # require a non-None api_key. The AuthInterceptor handles stripping
87
+ # the dummy Authorization header and injecting the real auth.
88
+ return LLM(
89
+ model=model_string,
66
90
  api_key="replaced",
67
- base_url=f"{url}/v1",
91
+ base_url=base_url,
92
+ interceptor=AuthInterceptor(),
93
+ **kwargs,
94
+ )
95
+ else:
96
+ # LiteLLM fallback: pass auth headers via extra_headers param.
97
+ # Omit api_key when auth uses X-Blaxel-Authorization to prevent
98
+ # litellm from adding "Authorization: Bearer replaced".
99
+ llm_api_key = "replaced" if "Authorization" in auth_headers else None
100
+ return LLM(
101
+ model=model_string,
102
+ api_key=llm_api_key,
103
+ base_url=base_url,
104
+ extra_headers=auth_headers,
68
105
  **kwargs,
69
106
  )
blaxel/crewai/tools.py CHANGED
@@ -1,10 +1,86 @@
1
+ from typing import Any
2
+
1
3
  from crewai.tools import BaseTool
4
+ from pydantic import BaseModel
2
5
 
3
6
  from blaxel.core.tools import bl_tools as bl_tools_core
4
- from blaxel.core.tools.common import create_model_from_json_schema
5
7
  from blaxel.core.tools.types import Tool
6
8
 
7
9
 
10
+ def _clean_schema_for_openai(schema: dict) -> dict:
11
+ """Clean JSON schema to be compatible with OpenAI strict mode.
12
+
13
+ Recursively resolves anyOf patterns, ensures all schemas have type keys,
14
+ removes additionalProperties and $schema, and ensures object types have
15
+ properties and required fields.
16
+ """
17
+ if not isinstance(schema, dict):
18
+ return schema
19
+
20
+ cleaned = schema.copy()
21
+
22
+ # Remove unsupported keys
23
+ cleaned.pop("$schema", None)
24
+ cleaned.pop("additionalProperties", None)
25
+
26
+ # Resolve anyOf: pick the non-null type
27
+ if "anyOf" in cleaned:
28
+ any_of = cleaned.pop("anyOf")
29
+ non_null = [s for s in any_of if s.get("type") != "null"]
30
+ if non_null:
31
+ # Merge the first non-null variant into current schema
32
+ resolved = _clean_schema_for_openai(non_null[0])
33
+ cleaned.update(resolved)
34
+ else:
35
+ cleaned["type"] = "string"
36
+
37
+ # Ensure type exists
38
+ if "type" not in cleaned and "properties" in cleaned:
39
+ cleaned["type"] = "object"
40
+
41
+ # Handle object types
42
+ if cleaned.get("type") == "object":
43
+ if "properties" not in cleaned:
44
+ cleaned["properties"] = {}
45
+ if "required" not in cleaned:
46
+ cleaned["required"] = list(cleaned["properties"].keys())
47
+
48
+ # Recursively clean properties
49
+ if "properties" in cleaned:
50
+ cleaned["properties"] = {
51
+ k: _clean_schema_for_openai(v) for k, v in cleaned["properties"].items()
52
+ }
53
+
54
+ # Recursively clean array items
55
+ if "items" in cleaned:
56
+ cleaned["items"] = _clean_schema_for_openai(cleaned["items"])
57
+ # Ensure items has a type
58
+ if "type" not in cleaned["items"]:
59
+ cleaned["items"]["type"] = "string"
60
+
61
+ return cleaned
62
+
63
+
64
+ def _make_clean_args_schema(tool: Tool) -> type[BaseModel]:
65
+ """Create a Pydantic model whose JSON schema returns the pre-cleaned schema.
66
+
67
+ CrewAI calls model_json_schema() on args_schema to build the OpenAI tool
68
+ parameters. By overriding model_json_schema we ensure the cleaned schema
69
+ is used directly, avoiding issues with Pydantic re-introducing anyOf or
70
+ dropping type keys on array items.
71
+ """
72
+ clean = _clean_schema_for_openai(tool.input_schema)
73
+
74
+ class CleanArgsSchema(BaseModel):
75
+ @classmethod
76
+ def model_json_schema(cls, *args: Any, **kwargs: Any) -> dict[str, Any]:
77
+ return clean
78
+
79
+ CleanArgsSchema.__name__ = f"{tool.name}Schema"
80
+ CleanArgsSchema.__qualname__ = f"{tool.name}Schema"
81
+ return CleanArgsSchema
82
+
83
+
8
84
  class CrewAITool(BaseTool):
9
85
  _tool: Tool
10
86
 
@@ -12,13 +88,20 @@ class CrewAITool(BaseTool):
12
88
  super().__init__(
13
89
  name=tool.name,
14
90
  description=tool.description,
15
- args_schema=create_model_from_json_schema(tool.input_schema),
91
+ args_schema=_make_clean_args_schema(tool),
16
92
  )
17
93
  self._tool = tool
18
94
 
19
95
  def _run(self, *args, **kwargs):
96
+ if not self._tool.sync_coroutine:
97
+ raise ValueError(f"Tool {self._tool.name} does not have a sync_coroutine defined")
20
98
  return self._tool.sync_coroutine(**kwargs)
21
99
 
100
+ async def _arun(self, *args, **kwargs):
101
+ if not self._tool.coroutine:
102
+ raise ValueError(f"Tool {self._tool.name} does not have a coroutine defined")
103
+ return await self._tool.coroutine(**kwargs)
104
+
22
105
 
23
106
  async def bl_tools(tools_names: list[str], **kwargs) -> list[BaseTool]:
24
107
  tools = bl_tools_core(tools_names, **kwargs)
blaxel/googleadk/model.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from logging import getLogger
2
2
 
3
- from google.adk.models.lite_llm import LiteLlm, LiteLLMClient
3
+ from google.adk.models.lite_llm import ( # type: ignore[import-not-found]
4
+ LiteLlm,
5
+ LiteLLMClient,
6
+ )
4
7
 
5
8
  from blaxel.core import bl_model as bl_model_core
6
9
  from blaxel.core import settings
@@ -23,7 +26,15 @@ class AuthenticatedLiteLLMClient(LiteLLMClient):
23
26
  Returns:
24
27
  The model response as a message.
25
28
  """
26
- kwargs["extra_headers"] = settings.auth.get_headers()
29
+ auth_headers = settings.auth.get_headers()
30
+ extra = dict(auth_headers)
31
+ # When auth uses X-Blaxel-Authorization (API keys), override the
32
+ # Authorization header that litellm sets from api_key or OPENAI_API_KEY
33
+ # env var. Without this, the server sees an invalid Authorization header
34
+ # and rejects the request.
35
+ if "Authorization" not in auth_headers:
36
+ extra["Authorization"] = ""
37
+ kwargs["extra_headers"] = extra
27
38
  return await super().acompletion(
28
39
  model=model,
29
40
  messages=messages,
@@ -44,7 +55,15 @@ class AuthenticatedLiteLLMClient(LiteLLMClient):
44
55
  Returns:
45
56
  The response from the model.
46
57
  """
47
- kwargs["extra_headers"] = settings.auth.get_headers()
58
+ auth_headers = settings.auth.get_headers()
59
+ extra = dict(auth_headers)
60
+ # When auth uses X-Blaxel-Authorization (API keys), override the
61
+ # Authorization header that litellm sets from api_key or OPENAI_API_KEY
62
+ # env var. Without this, the server sees an invalid Authorization header
63
+ # and rejects the request.
64
+ if "Authorization" not in auth_headers:
65
+ extra["Authorization"] = ""
66
+ kwargs["extra_headers"] = extra
48
67
  return super().completion(
49
68
  model=model,
50
69
  messages=messages,
blaxel/googleadk/tools.py CHANGED
@@ -1,8 +1,8 @@
1
1
  import inspect
2
- from typing import Any, override
2
+ from typing import Any
3
3
 
4
- from google.adk.tools import BaseTool, ToolContext
5
- from google.genai import types
4
+ from google.adk.tools import BaseTool, ToolContext # type: ignore[import-not-found]
5
+ from google.genai import types # type: ignore[import-not-found]
6
6
 
7
7
  from blaxel.core.tools import bl_tools as bl_tools_core
8
8
  from blaxel.core.tools.types import Tool
@@ -31,15 +31,33 @@ class GoogleADKTool(BaseTool):
31
31
  if "additionalProperties" in cleaned_schema:
32
32
  del cleaned_schema["additionalProperties"]
33
33
 
34
+ # Google genai Schema expects type as a single enum string (e.g. "STRING"),
35
+ # not a JSON Schema union list like ["null", "string"].
36
+ if "type" in cleaned_schema and isinstance(cleaned_schema["type"], list):
37
+ type_list = [t for t in cleaned_schema["type"] if t != "null"]
38
+ cleaned_schema["type"] = type_list[0].upper() if type_list else "STRING"
39
+ # Mark as nullable if "null" was in the original list
40
+ if "null" in schema["type"]:
41
+ cleaned_schema["nullable"] = True
42
+ elif "type" in cleaned_schema and isinstance(cleaned_schema["type"], str):
43
+ cleaned_schema["type"] = cleaned_schema["type"].upper()
44
+
45
+ # Ensure object types have properties
46
+ if cleaned_schema.get("type") == "OBJECT" and "properties" not in cleaned_schema:
47
+ cleaned_schema["properties"] = {}
48
+
34
49
  # Recursively clean properties if they exist
35
50
  if "properties" in cleaned_schema:
36
51
  cleaned_schema["properties"] = {
37
52
  k: self._clean_schema(v) for k, v in cleaned_schema["properties"].items()
38
53
  }
39
54
 
55
+ # Recursively clean items for array types
56
+ if "items" in cleaned_schema and isinstance(cleaned_schema["items"], dict):
57
+ cleaned_schema["items"] = self._clean_schema(cleaned_schema["items"])
58
+
40
59
  return cleaned_schema
41
60
 
42
- @override
43
61
  def _get_declaration(self) -> types.FunctionDeclaration | None:
44
62
  # Clean the schema recursively
45
63
  schema = self._clean_schema(self._tool.input_schema)
@@ -48,14 +66,15 @@ class GoogleADKTool(BaseTool):
48
66
  types.FunctionDeclaration(
49
67
  name=self._tool.name,
50
68
  description=self._tool.description,
51
- parameters=schema,
69
+ parameters=types.Schema(**schema),
52
70
  )
53
71
  )
54
72
 
55
73
  return function_decl
56
74
 
57
- @override
58
75
  async def run_async(self, *, args: dict[str, Any], tool_context: ToolContext) -> Any:
76
+ if not self._tool.coroutine:
77
+ raise ValueError(f"Tool {self._tool.name} does not have a coroutine defined")
59
78
  args_to_call = args.copy()
60
79
  signature = inspect.signature(self._tool.coroutine)
61
80
  if "tool_context" in signature.parameters:
@@ -23,16 +23,18 @@ from typing import (
23
23
 
24
24
  import httpx
25
25
  import requests
26
- from langchain_core.callbacks.manager import (
26
+ from langchain_core.callbacks.manager import ( # type: ignore[import-not-found]
27
27
  AsyncCallbackManagerForLLMRun,
28
28
  CallbackManagerForLLMRun,
29
29
  )
30
- from langchain_core.language_models import LanguageModelInput
31
- from langchain_core.language_models.chat_models import (
30
+ from langchain_core.language_models import ( # type: ignore[import-not-found]
31
+ LanguageModelInput,
32
+ )
33
+ from langchain_core.language_models.chat_models import ( # type: ignore[import-not-found]
32
34
  BaseChatModel,
33
35
  LangSmithParams,
34
36
  )
35
- from langchain_core.messages import (
37
+ from langchain_core.messages import ( # type: ignore[import-not-found]
36
38
  AIMessage,
37
39
  AIMessageChunk,
38
40
  BaseMessage,
@@ -41,25 +43,30 @@ from langchain_core.messages import (
41
43
  SystemMessage,
42
44
  ToolMessage,
43
45
  )
44
- from langchain_core.messages.ai import UsageMetadata
45
- from langchain_core.messages.tool import (
46
+ from langchain_core.messages.ai import UsageMetadata # type: ignore[import-not-found]
47
+ from langchain_core.messages.tool import ( # type: ignore[import-not-found]
46
48
  invalid_tool_call,
47
49
  tool_call,
48
50
  tool_call_chunk,
49
51
  )
50
- from langchain_core.output_parsers.openai_tools import (
52
+ from langchain_core.output_parsers.openai_tools import ( # type: ignore[import-not-found]
51
53
  JsonOutputKeyToolsParser,
52
54
  PydanticToolsParser,
53
55
  parse_tool_calls,
54
56
  )
55
- from langchain_core.outputs import (
57
+ from langchain_core.outputs import ( # type: ignore[import-not-found]
56
58
  ChatGeneration,
57
59
  ChatGenerationChunk,
58
60
  ChatResult,
59
61
  )
60
- from langchain_core.runnables import Runnable, RunnablePassthrough
61
- from langchain_core.tools import BaseTool
62
- from langchain_core.utils.function_calling import convert_to_openai_tool
62
+ from langchain_core.runnables import ( # type: ignore[import-not-found]
63
+ Runnable,
64
+ RunnablePassthrough,
65
+ )
66
+ from langchain_core.tools import BaseTool # type: ignore[import-not-found]
67
+ from langchain_core.utils.function_calling import ( # type: ignore[import-not-found]
68
+ convert_to_openai_tool,
69
+ )
63
70
  from PIL import Image
64
71
  from pydantic import BaseModel, ConfigDict, Field, SecretStr, model_validator
65
72
  from tenacity import (
@@ -1467,4 +1474,4 @@ def image_bytes_to_b64_string(image_bytes: bytes, image_format: str = "jpeg") ->
1467
1474
  """Convert image bytes to base64 string."""
1468
1475
  import base64
1469
1476
 
1470
- return f"data:image/{image_format};base64,{base64.b64encode(image_bytes).decode('utf-8')}"
1477
+ return f"data:image/{image_format};base64,{base64.b64encode(image_bytes).decode('utf-8')}"
blaxel/langgraph/model.py CHANGED
@@ -7,11 +7,15 @@ from blaxel.core import bl_model as bl_model_core
7
7
  from blaxel.core import settings
8
8
 
9
9
  if TYPE_CHECKING:
10
- from langchain_core.callbacks import Callbacks
11
- from langchain_core.language_models import LanguageModelInput
12
- from langchain_core.messages import BaseMessage
13
- from langchain_core.outputs import LLMResult
14
- from langchain_core.runnables import RunnableConfig
10
+ from langchain_core.callbacks import Callbacks # type: ignore[import-not-found]
11
+ from langchain_core.language_models import ( # type: ignore[import-not-found]
12
+ LanguageModelInput,
13
+ )
14
+ from langchain_core.messages import BaseMessage # type: ignore[import-not-found]
15
+ from langchain_core.outputs import LLMResult # type: ignore[import-not-found]
16
+ from langchain_core.runnables import ( # type: ignore[import-not-found]
17
+ RunnableConfig,
18
+ )
15
19
 
16
20
  logger = getLogger(__name__)
17
21
 
@@ -32,7 +36,7 @@ class TokenRefreshingWrapper:
32
36
  kwargs = config.get("kwargs", {})
33
37
 
34
38
  if model_type == "mistral":
35
- from langchain_openai import ChatOpenAI
39
+ from langchain_openai import ChatOpenAI # type: ignore[import-not-found]
36
40
 
37
41
  return ChatOpenAI(
38
42
  api_key=settings.auth.token,
@@ -41,7 +45,7 @@ class TokenRefreshingWrapper:
41
45
  **kwargs,
42
46
  )
43
47
  elif model_type == "cohere":
44
- from langchain_cohere import ChatCohere
48
+ from langchain_cohere import ChatCohere # type: ignore[import-not-found]
45
49
 
46
50
  return ChatCohere(
47
51
  cohere_api_key=settings.auth.token,
@@ -50,7 +54,7 @@ class TokenRefreshingWrapper:
50
54
  **kwargs,
51
55
  )
52
56
  elif model_type == "xai":
53
- from langchain_xai import ChatXAI
57
+ from langchain_xai import ChatXAI # type: ignore[import-not-found]
54
58
 
55
59
  return ChatXAI(
56
60
  model=model,
@@ -59,7 +63,9 @@ class TokenRefreshingWrapper:
59
63
  **kwargs,
60
64
  )
61
65
  elif model_type == "deepseek":
62
- from langchain_deepseek import ChatDeepSeek
66
+ from langchain_deepseek import ( # type: ignore[import-not-found]
67
+ ChatDeepSeek,
68
+ )
63
69
 
64
70
  return ChatDeepSeek(
65
71
  api_key=settings.auth.token,
@@ -68,7 +74,9 @@ class TokenRefreshingWrapper:
68
74
  **kwargs,
69
75
  )
70
76
  elif model_type == "anthropic":
71
- from langchain_anthropic import ChatAnthropic
77
+ from langchain_anthropic import ( # type: ignore[import-not-found]
78
+ ChatAnthropic,
79
+ )
72
80
 
73
81
  return ChatAnthropic(
74
82
  api_key=settings.auth.token,
@@ -78,7 +86,9 @@ class TokenRefreshingWrapper:
78
86
  **kwargs,
79
87
  )
80
88
  elif model_type == "gemini":
81
- from .custom.gemini import ChatGoogleGenerativeAI
89
+ from .custom.gemini import (
90
+ ChatGoogleGenerativeAI, # type: ignore[import-not-found]
91
+ )
82
92
 
83
93
  return ChatGoogleGenerativeAI(
84
94
  model=model,
@@ -88,7 +98,9 @@ class TokenRefreshingWrapper:
88
98
  **kwargs,
89
99
  )
90
100
  elif model_type == "cerebras":
91
- from langchain_cerebras import ChatCerebras
101
+ from langchain_cerebras import ( # type: ignore[import-not-found]
102
+ ChatCerebras,
103
+ )
92
104
 
93
105
  return ChatCerebras(
94
106
  api_key=settings.auth.token,
@@ -97,7 +109,7 @@ class TokenRefreshingWrapper:
97
109
  **kwargs,
98
110
  )
99
111
  else:
100
- from langchain_openai import ChatOpenAI
112
+ from langchain_openai import ChatOpenAI # type: ignore[import-not-found]
101
113
 
102
114
  if model_type != "openai":
103
115
  logger.warning(f"Model {model} is not supported by Langchain, defaulting to OpenAI")
@@ -113,10 +125,6 @@ class TokenRefreshingWrapper:
113
125
  # Only refresh if using ClientCredentials (which has get_token method)
114
126
  current_token = settings.auth.token
115
127
 
116
- if hasattr(settings.auth, "get_token"):
117
- # This will trigger token refresh if needed
118
- settings.auth.get_token()
119
-
120
128
  new_token = settings.auth.token
121
129
 
122
130
  # If token changed, recreate the model
@@ -251,4 +259,4 @@ async def bl_model(name: str, **kwargs):
251
259
  model_config = {"type": type, "model": model, "url": url, "kwargs": kwargs}
252
260
 
253
261
  # Create and return the wrapper
254
- return TokenRefreshingChatModel(model_config)
262
+ return TokenRefreshingChatModel(model_config)
blaxel/langgraph/tools.py CHANGED
@@ -4,7 +4,7 @@ from blaxel.core.tools import bl_tools as bl_tools_core
4
4
  from blaxel.core.tools.types import Tool, ToolException
5
5
 
6
6
  if TYPE_CHECKING:
7
- from langchain_core.tools import StructuredTool
7
+ from langchain_core.tools import StructuredTool # type: ignore[import-not-found]
8
8
 
9
9
 
10
10
  def _clean_schema_for_openai(schema: Dict[str, Any]) -> Dict[str, Any]:
@@ -37,19 +37,14 @@ def _clean_schema_for_openai(schema: Dict[str, Any]) -> Dict[str, Any]:
37
37
 
38
38
 
39
39
  def get_langchain_tool(tool: Tool) -> "StructuredTool":
40
- from langchain_core.tools import StructuredTool
41
- from mcp.types import (
42
- CallToolResult,
43
- EmbeddedResource,
44
- ImageContent,
45
- TextContent,
46
- )
47
-
48
- NonTextContent = ImageContent | EmbeddedResource
40
+ from langchain_core.tools import StructuredTool # type: ignore[import-not-found]
41
+ from mcp.types import CallToolResult, EmbeddedResource, ImageContent, TextContent
49
42
 
50
43
  async def langchain_coroutine(
51
44
  **arguments: dict[str, Any],
52
- ) -> tuple[str | list[str], list[NonTextContent] | None]:
45
+ ) -> tuple[str | list[str], list[ImageContent | EmbeddedResource] | None]:
46
+ if not tool.coroutine:
47
+ raise ValueError(f"Tool {tool.name} does not have a coroutine defined")
53
48
  result: CallToolResult = await tool.coroutine(**arguments)
54
49
  text_contents: list[TextContent] = []
55
50
  non_text_contents = []
blaxel/livekit/model.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from logging import getLogger
2
2
 
3
3
  import httpx
4
- from livekit.plugins import openai
5
- from openai import AsyncOpenAI
4
+ from livekit.plugins import openai # type: ignore[import-not-found]
5
+ from openai import AsyncOpenAI # type: ignore[import-not-found]
6
6
 
7
7
  from blaxel.core import bl_model as bl_model_core
8
8
  from blaxel.core import settings
@@ -20,6 +20,11 @@ class DynamicHeadersHTTPClient(httpx.AsyncClient):
20
20
  async def send(self, request, *args, **kwargs):
21
21
  # Update headers with the latest auth headers before each request
22
22
  auth_headers = settings.auth.get_headers()
23
+ # Remove the SDK's default "Authorization: Bearer replaced" header
24
+ # when our auth uses a different header (e.g. X-Blaxel-Authorization with API keys)
25
+ if "Authorization" not in auth_headers:
26
+ request.headers.pop("Authorization", None)
27
+ request.headers.pop("authorization", None)
23
28
  for key, value in auth_headers.items():
24
29
  request.headers[key] = value
25
30
  return await super().send(request, *args, **kwargs)
blaxel/livekit/tools.py CHANGED
@@ -1,4 +1,4 @@
1
- from livekit.agents import function_tool, llm
1
+ from livekit.agents import function_tool, llm # type: ignore[import-not-found]
2
2
 
3
3
  from blaxel.core.tools import bl_tools as bl_tools_core
4
4
  from blaxel.core.tools.types import Tool
@@ -6,6 +6,8 @@ from blaxel.core.tools.types import Tool
6
6
 
7
7
  def livekit_coroutine(tool: Tool):
8
8
  async def livekit_coroutine_wrapper(raw_arguments: dict[str, object]):
9
+ if not tool.coroutine:
10
+ raise ValueError(f"Tool {tool.name} does not have a coroutine defined")
9
11
  result = await tool.coroutine(**raw_arguments)
10
12
  return result.model_dump_json()
11
13
 
@@ -11,7 +11,7 @@ from blaxel.core import settings
11
11
  os.environ["TRANSFORMERS_NO_ADVISORY_WARNINGS"] = "1"
12
12
 
13
13
  if TYPE_CHECKING:
14
- from llama_index.core.base.llms.types import (
14
+ from llama_index.core.base.llms.types import ( # type: ignore[import-not-found]
15
15
  ChatMessage,
16
16
  ChatResponse,
17
17
  ChatResponseAsyncGen,
@@ -20,13 +20,19 @@ if TYPE_CHECKING:
20
20
  CompletionResponseAsyncGen,
21
21
  CompletionResponseGen,
22
22
  )
23
- from llama_index.core.llms.llm import ToolSelection
24
- from llama_index.core.tools.types import BaseTool
23
+ from llama_index.core.llms.llm import ( # type: ignore[import-not-found]
24
+ ToolSelection,
25
+ )
26
+ from llama_index.core.tools.types import BaseTool # type: ignore[import-not-found]
25
27
 
26
28
  # Runtime imports needed for class inheritance and construction
27
- from llama_index.core.base.llms.types import LLMMetadata # noqa: E402
28
- from llama_index.core.llms.function_calling import FunctionCallingLLM # noqa: E402
29
- from pydantic import PrivateAttr # noqa: E402
29
+ from llama_index.core.base.llms.types import ( # type: ignore[import-not-found]
30
+ LLMMetadata,
31
+ )
32
+ from llama_index.core.llms.function_calling import ( # type: ignore[import-not-found]
33
+ FunctionCallingLLM,
34
+ )
35
+ from pydantic import PrivateAttr # type: ignore[import-not-found]
30
36
 
31
37
  logger = getLogger(__name__)
32
38
 
@@ -81,7 +87,9 @@ class TokenRefreshingLLM(FunctionCallingLLM):
81
87
  kwargs = config.get("kwargs", {})
82
88
 
83
89
  if model_type == "anthropic":
84
- from llama_index.llms.anthropic import Anthropic
90
+ from llama_index.llms.anthropic import ( # type: ignore[import-not-found]
91
+ Anthropic,
92
+ )
85
93
 
86
94
  return Anthropic(
87
95
  model=model,
@@ -91,7 +99,7 @@ class TokenRefreshingLLM(FunctionCallingLLM):
91
99
  **kwargs,
92
100
  )
93
101
  elif model_type == "xai":
94
- from llama_index.llms.groq import Groq
102
+ from llama_index.llms.groq import Groq # type: ignore[import-not-found]
95
103
 
96
104
  return Groq(
97
105
  model=model,
@@ -101,7 +109,9 @@ class TokenRefreshingLLM(FunctionCallingLLM):
101
109
  )
102
110
  elif model_type == "gemini":
103
111
  from google.genai.types import HttpOptions
104
- from llama_index.llms.google_genai import GoogleGenAI
112
+ from llama_index.llms.google_genai import ( # type: ignore[import-not-found]
113
+ GoogleGenAI,
114
+ )
105
115
 
106
116
  return GoogleGenAI(
107
117
  api_key=settings.auth.token,
@@ -114,11 +124,13 @@ class TokenRefreshingLLM(FunctionCallingLLM):
114
124
  **kwargs,
115
125
  )
116
126
  elif model_type == "cohere":
117
- from .custom.cohere import Cohere
127
+ from .custom.cohere import Cohere # type: ignore[import-not-found]
118
128
 
119
129
  return Cohere(model=model, api_key=settings.auth.token, api_base=url, **kwargs)
120
130
  elif model_type == "deepseek":
121
- from llama_index.llms.deepseek import DeepSeek
131
+ from llama_index.llms.deepseek import ( # type: ignore[import-not-found]
132
+ DeepSeek,
133
+ )
122
134
 
123
135
  return DeepSeek(
124
136
  model=model,
@@ -127,11 +139,15 @@ class TokenRefreshingLLM(FunctionCallingLLM):
127
139
  **kwargs,
128
140
  )
129
141
  elif model_type == "mistral":
130
- from llama_index.llms.mistralai import MistralAI
142
+ from llama_index.llms.mistralai import ( # type: ignore[import-not-found]
143
+ MistralAI,
144
+ )
131
145
 
132
146
  return MistralAI(model=model, api_key=settings.auth.token, endpoint=url, **kwargs)
133
147
  elif model_type == "cerebras":
134
- from llama_index.llms.cerebras import Cerebras
148
+ from llama_index.llms.cerebras import ( # type: ignore[import-not-found]
149
+ Cerebras,
150
+ )
135
151
 
136
152
  return Cerebras(
137
153
  model=model,
@@ -140,7 +156,7 @@ class TokenRefreshingLLM(FunctionCallingLLM):
140
156
  **kwargs,
141
157
  )
142
158
  else:
143
- from llama_index.llms.openai import OpenAI
159
+ from llama_index.llms.openai import OpenAI # type: ignore[import-not-found]
144
160
 
145
161
  if model_type != "openai":
146
162
  logger.warning(
@@ -265,4 +281,4 @@ async def bl_model(name, **kwargs):
265
281
  model_config = {"type": type, "model": model, "url": url, "kwargs": kwargs}
266
282
 
267
283
  # Create and return the wrapper
268
- return TokenRefreshingLLM(model_config)
284
+ return TokenRefreshingLLM(model_config)
@@ -5,12 +5,14 @@ from blaxel.core.tools.common import create_model_from_json_schema
5
5
  from blaxel.core.tools.types import Tool
6
6
 
7
7
  if TYPE_CHECKING:
8
- from llama_index.core.tools import FunctionTool
8
+ from llama_index.core.tools import FunctionTool # type: ignore[import-not-found]
9
9
 
10
10
 
11
11
  def get_llamaindex_tool(tool: Tool) -> "FunctionTool":
12
- from llama_index.core.tools import FunctionTool
13
- from llama_index.core.tools.types import ToolMetadata
12
+ from llama_index.core.tools import FunctionTool # type: ignore[import-not-found]
13
+ from llama_index.core.tools.types import ( # type: ignore[import-not-found]
14
+ ToolMetadata,
15
+ )
14
16
 
15
17
  model_schema = create_model_from_json_schema(
16
18
  tool.input_schema, model_name=f"{tool.name}_Schema"
@@ -29,4 +31,4 @@ def get_llamaindex_tool(tool: Tool) -> "FunctionTool":
29
31
  async def bl_tools(tools_names: list[str], **kwargs) -> list["FunctionTool"]:
30
32
  tools = bl_tools_core(tools_names, **kwargs)
31
33
  await tools.initialize()
32
- return [get_llamaindex_tool(tool) for tool in tools.get_tools()]
34
+ return [get_llamaindex_tool(tool) for tool in tools.get_tools()]
blaxel/openai/model.py CHANGED
@@ -1,5 +1,6 @@
1
1
  import httpx
2
- from agents import AsyncOpenAI, OpenAIChatCompletionsModel
2
+ from agents import OpenAIChatCompletionsModel
3
+ from openai import AsyncOpenAI
3
4
 
4
5
  from blaxel.core import bl_model as bl_model_core
5
6
  from blaxel.core import settings
@@ -14,6 +15,11 @@ class DynamicHeadersHTTPClient(httpx.AsyncClient):
14
15
  async def send(self, request, *args, **kwargs):
15
16
  # Update headers with the latest auth headers before each request
16
17
  auth_headers = settings.auth.get_headers()
18
+ # Remove the SDK's default "Authorization: Bearer replaced" header
19
+ # when our auth uses a different header (e.g. X-Blaxel-Authorization with API keys)
20
+ if "Authorization" not in auth_headers:
21
+ request.headers.pop("Authorization", None)
22
+ request.headers.pop("authorization", None)
17
23
  for key, value in auth_headers.items():
18
24
  request.headers[key] = value
19
25
  return await super().send(request, *args, **kwargs)
blaxel/openai/tools.py CHANGED
@@ -1,7 +1,8 @@
1
1
  import json
2
2
  from typing import Any
3
3
 
4
- from agents import FunctionTool, RunContextWrapper
4
+ from agents import FunctionTool # type: ignore[import-not-found]
5
+ from agents.tool_context import ToolContext # type: ignore[import-not-found]
5
6
 
6
7
  from blaxel.core.tools import bl_tools as bl_tools_core
7
8
  from blaxel.core.tools.types import Tool
@@ -24,6 +25,13 @@ def _clean_schema_for_openai(schema: dict) -> dict:
24
25
  if "additionalProperties" in cleaned_schema:
25
26
  del cleaned_schema["additionalProperties"]
26
27
 
28
+ # Ensure object type schemas have properties
29
+ if cleaned_schema.get("type") == "object":
30
+ if "properties" not in cleaned_schema:
31
+ cleaned_schema["properties"] = {}
32
+ if "required" not in cleaned_schema:
33
+ cleaned_schema["required"] = []
34
+
27
35
  # Recursively clean properties if they exist
28
36
  if "properties" in cleaned_schema:
29
37
  cleaned_schema["properties"] = {
@@ -39,9 +47,11 @@ def _clean_schema_for_openai(schema: dict) -> dict:
39
47
 
40
48
  def get_openai_tool(tool: Tool) -> FunctionTool:
41
49
  async def openai_coroutine(
42
- _: RunContextWrapper,
43
- arguments: dict[str, Any],
50
+ _: ToolContext[Any],
51
+ arguments: str,
44
52
  ) -> Any:
53
+ if not tool.coroutine:
54
+ raise ValueError(f"Tool {tool.name} does not have a coroutine defined")
45
55
  result = await tool.coroutine(**json.loads(arguments))
46
56
  return result
47
57
 
blaxel/pydantic/model.py CHANGED
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  from typing import Any
3
3
 
4
- from pydantic_ai.models import Model
4
+ from pydantic_ai.models import Model # type: ignore[import-not-found]
5
5
 
6
6
  from blaxel.core import bl_model as bl_model_core
7
7
  from blaxel.core import settings
@@ -30,8 +30,12 @@ class TokenRefreshingModel(Model):
30
30
 
31
31
  if type == "mistral":
32
32
  from mistralai.sdk import Mistral
33
- from pydantic_ai.models.mistral import MistralModel
34
- from pydantic_ai.providers.mistral import MistralProvider
33
+ from pydantic_ai.models.mistral import ( # type: ignore[import-not-found]
34
+ MistralModel,
35
+ )
36
+ from pydantic_ai.providers.mistral import ( # type: ignore[import-not-found]
37
+ MistralProvider,
38
+ )
35
39
 
36
40
  return MistralModel(
37
41
  model_name=model,
@@ -45,8 +49,12 @@ class TokenRefreshingModel(Model):
45
49
  )
46
50
  elif type == "cohere":
47
51
  from cohere import AsyncClientV2
48
- from pydantic_ai.models.cohere import CohereModel
49
- from pydantic_ai.providers.cohere import CohereProvider
52
+ from pydantic_ai.models.cohere import ( # type: ignore[import-not-found]
53
+ CohereModel,
54
+ )
55
+ from pydantic_ai.providers.cohere import ( # type: ignore[import-not-found]
56
+ CohereProvider,
57
+ )
50
58
 
51
59
  return CohereModel(
52
60
  model_name=model,
@@ -58,30 +66,42 @@ class TokenRefreshingModel(Model):
58
66
  ),
59
67
  )
60
68
  elif type == "xai":
61
- from pydantic_ai.models.openai import OpenAIModel
62
- from pydantic_ai.providers.openai import OpenAIProvider
69
+ from pydantic_ai.models.openai import ( # type: ignore[import-not-found]
70
+ OpenAIChatModel,
71
+ )
72
+ from pydantic_ai.providers.openai import ( # type: ignore[import-not-found]
73
+ OpenAIProvider,
74
+ )
63
75
 
64
- return OpenAIModel(
76
+ return OpenAIChatModel(
65
77
  model_name=model,
66
78
  provider=OpenAIProvider(
67
79
  base_url=f"{url}/v1", api_key=settings.auth.token, **kwargs
68
80
  ),
69
81
  )
70
82
  elif type == "deepseek":
71
- from pydantic_ai.models.openai import OpenAIModel
72
- from pydantic_ai.providers.openai import OpenAIProvider
83
+ from pydantic_ai.models.openai import ( # type: ignore[import-not-found]
84
+ OpenAIChatModel,
85
+ )
86
+ from pydantic_ai.providers.openai import ( # type: ignore[import-not-found]
87
+ OpenAIProvider,
88
+ )
73
89
 
74
- return OpenAIModel(
90
+ return OpenAIChatModel(
75
91
  model_name=model,
76
92
  provider=OpenAIProvider(
77
93
  base_url=f"{url}/v1", api_key=settings.auth.token, **kwargs
78
94
  ),
79
95
  )
80
96
  elif type == "cerebras":
81
- from pydantic_ai.models.openai import OpenAIModel
82
- from pydantic_ai.providers.openai import OpenAIProvider
97
+ from pydantic_ai.models.openai import ( # type: ignore[import-not-found]
98
+ OpenAIChatModel,
99
+ )
100
+ from pydantic_ai.providers.openai import ( # type: ignore[import-not-found]
101
+ OpenAIProvider,
102
+ )
83
103
 
84
- return OpenAIModel(
104
+ return OpenAIChatModel(
85
105
  model_name=model,
86
106
  provider=OpenAIProvider(
87
107
  base_url=f"{url}/v1", api_key=settings.auth.token, **kwargs
@@ -116,12 +136,12 @@ class TokenRefreshingModel(Model):
116
136
  ),
117
137
  )
118
138
  else:
119
- from pydantic_ai.models.openai import OpenAIModel
139
+ from pydantic_ai.models.openai import OpenAIChatModel
120
140
  from pydantic_ai.providers.openai import OpenAIProvider
121
141
 
122
142
  if type != "openai":
123
143
  logger.warning(f"Model {model} is not supported by Pydantic, defaulting to OpenAI")
124
- return OpenAIModel(
144
+ return OpenAIChatModel(
125
145
  model_name=model,
126
146
  provider=OpenAIProvider(
127
147
  base_url=f"{url}/v1", api_key=settings.auth.token, **kwargs
@@ -130,12 +150,6 @@ class TokenRefreshingModel(Model):
130
150
 
131
151
  def _get_fresh_model(self) -> Model:
132
152
  """Get or create a model with fresh token if needed."""
133
- # Only refresh if using ClientCredentials (which has get_token method)
134
- if hasattr(settings.auth, "get_token"):
135
- # This will trigger token refresh if needed
136
- logger.debug(f"Calling get_token for {self.model_config['type']} model")
137
- settings.auth.get_token()
138
-
139
153
  new_token = settings.auth.token
140
154
 
141
155
  # If token changed or no cached model, create new one
@@ -152,10 +166,10 @@ class TokenRefreshingModel(Model):
152
166
  return model.model_name
153
167
 
154
168
  @property
155
- def system(self) -> Any | None:
169
+ def system(self) -> str:
156
170
  """Return the system property from the wrapped model."""
157
171
  model = self._get_fresh_model()
158
- return model.system if hasattr(model, "system") else None
172
+ return model.system if hasattr(model, "system") else ""
159
173
 
160
174
  async def request(self, *args, **kwargs):
161
175
  """Make a request to the model with token refresh."""
blaxel/pydantic/tools.py CHANGED
@@ -1,11 +1,44 @@
1
- from pydantic_ai import RunContext
2
- from pydantic_ai.tools import Tool as PydanticTool
3
- from pydantic_ai.tools import ToolDefinition
1
+ from typing import Any
2
+
3
+ from pydantic_ai import RunContext # type: ignore[import-not-found]
4
+ from pydantic_ai.tools import Tool as PydanticTool # type: ignore[import-not-found]
5
+ from pydantic_ai.tools import ToolDefinition # type: ignore[import-not-found]
4
6
 
5
7
  from blaxel.core.tools import Tool
6
8
  from blaxel.core.tools import bl_tools as bl_tools_core
7
9
 
8
10
 
11
+ def _clean_schema_for_openai(schema: dict[str, Any]) -> dict[str, Any]:
12
+ """Clean JSON schema to be compatible with OpenAI function calling.
13
+
14
+ OpenAI requires object schemas to have a 'properties' field, even if empty.
15
+ """
16
+ if not isinstance(schema, dict):
17
+ return schema
18
+
19
+ cleaned = schema.copy()
20
+
21
+ if cleaned.get("type") == "object":
22
+ if "properties" not in cleaned:
23
+ cleaned["properties"] = {}
24
+ if "required" not in cleaned:
25
+ cleaned["required"] = []
26
+
27
+ if "additionalProperties" in cleaned:
28
+ del cleaned["additionalProperties"]
29
+ if "$schema" in cleaned:
30
+ del cleaned["$schema"]
31
+
32
+ if "properties" in cleaned:
33
+ cleaned["properties"] = {
34
+ k: _clean_schema_for_openai(v) for k, v in cleaned["properties"].items()
35
+ }
36
+ if "items" in cleaned and isinstance(cleaned["items"], dict):
37
+ cleaned["items"] = _clean_schema_for_openai(cleaned["items"])
38
+
39
+ return cleaned
40
+
41
+
9
42
  def get_pydantic_tool(tool: Tool) -> PydanticTool:
10
43
  """
11
44
  Converts a custom Tool object into a Pydantic AI Tool object.
@@ -27,7 +60,7 @@ def get_pydantic_tool(tool: Tool) -> PydanticTool:
27
60
  """Dynamically prepares the ToolDefinition using the custom Tool's attributes."""
28
61
  tool_def.name = tool.name # Override inferred name
29
62
  tool_def.description = tool.description # Override inferred description
30
- tool_def.parameters_json_schema = tool.input_schema
63
+ tool_def.parameters_json_schema = _clean_schema_for_openai(tool.input_schema)
31
64
  return tool_def
32
65
 
33
66
  async def pydantic_function(**kwargs):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blaxel
3
- Version: 0.2.38rc122
3
+ Version: 0.2.39
4
4
  Summary: Blaxel - AI development platform SDK
5
5
  Project-URL: Homepage, https://blaxel.ai
6
6
  Project-URL: Documentation, https://docs.blaxel.ai
@@ -17,52 +17,11 @@ Requires-Dist: pyjwt>=2.0.0
17
17
  Requires-Dist: python-dateutil>=2.8.0
18
18
  Requires-Dist: pyyaml>=6.0.0
19
19
  Requires-Dist: requests>=2.32.3
20
- Requires-Dist: tomli>=2.2.1
20
+ Requires-Dist: tomli>=2.0.2
21
21
  Requires-Dist: websockets<16.0.0
22
- Provides-Extra: all
23
- Requires-Dist: crewai==0.159.0; extra == 'all'
24
- Requires-Dist: google-adk>=1.4.0; extra == 'all'
25
- Requires-Dist: langchain-anthropic>=0.3.10; extra == 'all'
26
- Requires-Dist: langchain-cerebras<0.6.0,>=0.5.0; extra == 'all'
27
- Requires-Dist: langchain-cohere>=0.4.3; extra == 'all'
28
- Requires-Dist: langchain-community<0.4.0,>=0.3.3; extra == 'all'
29
- Requires-Dist: langchain-core<0.4.0,>=0.3.13; extra == 'all'
30
- Requires-Dist: langchain-deepseek-official>=0.1.0.post1; extra == 'all'
31
- Requires-Dist: langchain-openai>=0.3.10; extra == 'all'
32
- Requires-Dist: langchain-xai>=0.2.2; extra == 'all'
33
- Requires-Dist: langgraph<0.3.0,>=0.2.40; extra == 'all'
34
- Requires-Dist: litellm==1.74.9; extra == 'all'
35
- Requires-Dist: litellm>=1.63.11; extra == 'all'
36
- Requires-Dist: livekit-agents[anthropic,cartesia,deepgram,elevenlabs,groq,openai,silero,turn-detector]~=1.0; extra == 'all'
37
- Requires-Dist: livekit-plugins-noise-cancellation~=0.2; extra == 'all'
38
- Requires-Dist: llama-index-llms-anthropic>=0.6.14; extra == 'all'
39
- Requires-Dist: llama-index-llms-cerebras>=0.2.2; extra == 'all'
40
- Requires-Dist: llama-index-llms-cohere>=0.4.1; extra == 'all'
41
- Requires-Dist: llama-index-llms-deepseek>=0.1.1; extra == 'all'
42
- Requires-Dist: llama-index-llms-google-genai>=0.1.13; extra == 'all'
43
- Requires-Dist: llama-index-llms-groq>=0.3.1; extra == 'all'
44
- Requires-Dist: llama-index-llms-mistralai>=0.4.0; extra == 'all'
45
- Requires-Dist: llama-index-llms-openai>=0.3.42; extra == 'all'
46
- Requires-Dist: llama-index>=0.12.46; extra == 'all'
47
- Requires-Dist: openai-agents>=0.0.19; extra == 'all'
48
- Requires-Dist: openai==1.99.9; extra == 'all'
49
- Requires-Dist: opentelemetry-exporter-otlp>=1.28.0; extra == 'all'
50
- Requires-Dist: opentelemetry-instrumentation-anthropic==0.41.0; extra == 'all'
51
- Requires-Dist: opentelemetry-instrumentation-cohere==0.41.0; extra == 'all'
52
- Requires-Dist: opentelemetry-instrumentation-crewai==0.41.0; extra == 'all'
53
- Requires-Dist: opentelemetry-instrumentation-fastapi==0.56b0; extra == 'all'
54
- Requires-Dist: opentelemetry-instrumentation-google-generativeai==0.41.0; extra == 'all'
55
- Requires-Dist: opentelemetry-instrumentation-langchain>=0.35.0; extra == 'all'
56
- Requires-Dist: opentelemetry-instrumentation-llamaindex>=0.40.7; extra == 'all'
57
- Requires-Dist: opentelemetry-instrumentation-ollama==0.41.0; extra == 'all'
58
- Requires-Dist: opentelemetry-instrumentation-openai==0.41.0; extra == 'all'
59
- Requires-Dist: pillow>=10.0.0; extra == 'all'
60
- Requires-Dist: pydantic-ai>=0.0.48; extra == 'all'
61
22
  Provides-Extra: core
62
23
  Provides-Extra: crewai
63
- Requires-Dist: crewai==0.159.0; extra == 'crewai'
64
- Requires-Dist: litellm==1.74.9; extra == 'crewai'
65
- Requires-Dist: openai==1.99.9; extra == 'crewai'
24
+ Requires-Dist: crewai[litellm]==1.9.3; extra == 'crewai'
66
25
  Requires-Dist: opentelemetry-instrumentation-crewai==0.41.0; extra == 'crewai'
67
26
  Provides-Extra: dev
68
27
  Requires-Dist: pyright; extra == 'dev'
@@ -96,17 +55,17 @@ Requires-Dist: llama-index-llms-google-genai>=0.1.13; extra == 'llamaindex'
96
55
  Requires-Dist: llama-index-llms-groq>=0.3.1; extra == 'llamaindex'
97
56
  Requires-Dist: llama-index-llms-mistralai>=0.4.0; extra == 'llamaindex'
98
57
  Requires-Dist: llama-index-llms-openai>=0.3.42; extra == 'llamaindex'
99
- Requires-Dist: llama-index>=0.12.46; extra == 'llamaindex'
58
+ Requires-Dist: llama-index>=0.14.13; extra == 'llamaindex'
100
59
  Requires-Dist: opentelemetry-instrumentation-llamaindex>=0.40.7; extra == 'llamaindex'
101
60
  Provides-Extra: openai
102
61
  Requires-Dist: openai-agents>=0.0.19; extra == 'openai'
103
62
  Provides-Extra: pydantic
104
- Requires-Dist: pydantic-ai>=0.0.48; extra == 'pydantic'
63
+ Requires-Dist: pydantic-ai>=1.0.8; extra == 'pydantic'
105
64
  Provides-Extra: telemetry
106
65
  Requires-Dist: opentelemetry-exporter-otlp>=1.28.0; extra == 'telemetry'
107
66
  Requires-Dist: opentelemetry-instrumentation-anthropic==0.41.0; extra == 'telemetry'
108
67
  Requires-Dist: opentelemetry-instrumentation-cohere==0.41.0; extra == 'telemetry'
109
- Requires-Dist: opentelemetry-instrumentation-fastapi==0.56b0; extra == 'telemetry'
68
+ Requires-Dist: opentelemetry-instrumentation-fastapi==0.55b0; extra == 'telemetry'
110
69
  Requires-Dist: opentelemetry-instrumentation-google-generativeai==0.41.0; extra == 'telemetry'
111
70
  Requires-Dist: opentelemetry-instrumentation-ollama==0.41.0; extra == 'telemetry'
112
71
  Requires-Dist: opentelemetry-instrumentation-openai==0.41.0; extra == 'telemetry'
@@ -1,10 +1,10 @@
1
- blaxel/__init__.py,sha256=CJtHkLCfIuSmNEqG5afzbxuceMG5h0ve2VADXBGizWs,421
1
+ blaxel/__init__.py,sha256=QTfkxywUPHoSPf0P4W1ZlvAXblBsMkxtuduqXT78Lgo,413
2
2
  blaxel/core/__init__.py,sha256=CU0gXpVRbuQZNWoCJuuhZS0ZhXPEu0cg-3XzoYMrBm4,1756
3
3
  blaxel/core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  blaxel/core/agents/__init__.py,sha256=MJZga99lU8JWUUPHd4rmUfdo7ALwWgF7CQq95SfT2OI,4456
5
5
  blaxel/core/authentication/__init__.py,sha256=5uHv73xZ2aZRXzGHc1sP-XWqZUryfXxfC9dMX7-gd6o,3241
6
6
  blaxel/core/authentication/apikey.py,sha256=k56FGvjoniW2BzdFqNNKzsfnu8Jvh8fvNZlwDq_78Y4,1286
7
- blaxel/core/authentication/clientcredentials.py,sha256=Ph-S_LQCoHrYgUfrcnw-TI5x5wL-lbgNzPTmholuKk0,3903
7
+ blaxel/core/authentication/clientcredentials.py,sha256=tXY-PKOMyZzqx4D-T20PJNJekUBGiEamEv8Q6W36h-g,3853
8
8
  blaxel/core/authentication/devicemode.py,sha256=SVCBEZzL0d74JPln9lJFsngA2RsNW1RqgbORSpVbEIg,6064
9
9
  blaxel/core/authentication/oauth.py,sha256=01yaAXytxqgsXUsSXhJzfI5-WpeU9RtrruZiimmMKf4,1453
10
10
  blaxel/core/authentication/types.py,sha256=0AmkmiPq9THT8a9bDC645rLE8mnEKb_BHUsYmYn0JAQ,1573
@@ -423,41 +423,41 @@ blaxel/core/sandbox/sync/process.py,sha256=0sDCTBp7lANzBkzB560pJnRvRz4yopnsLYj_g
423
423
  blaxel/core/sandbox/sync/sandbox.py,sha256=pbACjLaWy69imTZ2wtPA6eQeGFt5JpVuT4Fi4GPBbdU,15476
424
424
  blaxel/core/sandbox/sync/session.py,sha256=e0CVbW2LBRYTwm4RL52S0UdNvhNfuFLo6AYE5hk9DH0,4931
425
425
  blaxel/core/sandbox/sync/system.py,sha256=VDmO32qFVkURNZySvncYT2KBqu5Rj8cxT6qT2nMn0-k,2713
426
- blaxel/core/tools/__init__.py,sha256=OK2TFqeXAIi6CC7xtL8fFl-4DvCB7jjihkhx6RTld_c,13147
426
+ blaxel/core/tools/__init__.py,sha256=v9Ef8MjPElJiSkq2BlS9yo0mYzyHpJCzNgW7352pcns,13391
427
427
  blaxel/core/tools/common.py,sha256=dAjDRaI2psAoHPqKeUzyqab3N6blgkD-HO0gPMpIzCE,1878
428
428
  blaxel/core/tools/types.py,sha256=EXa-10iOOXvd8zB2IsTS0gWrgoC2hqbIv6iK5m6E5t8,693
429
429
  blaxel/core/volume/__init__.py,sha256=jZgddogAPJGQnD2OoTcDb9zLqxeT6yYpfC-i1yKUVUM,253
430
- blaxel/core/volume/volume.py,sha256=exCPH3llsjQDRH9MWsGwp-JXqJfumZAFD8APhmsY6oI,22653
430
+ blaxel/core/volume/volume.py,sha256=jY_BrSEw063TjpX8aLIhfhKpw07NdgMMGYZIa4LhXIc,22728
431
431
  blaxel/crewai/__init__.py,sha256=HdAZxRDgC2zW-O5xYzb_Xuysb66_fMykNJVPxlTmFnY,123
432
- blaxel/crewai/model.py,sha256=iR-40o8pGBFrktYNKcTaMdqbF_F1SFVavNOhhi2fW3w,1957
432
+ blaxel/crewai/model.py,sha256=mH8iKTwALRDT3IrDbiHW5zyDv9vFl0u4OX3hXPmC7mI,3973
433
433
  blaxel/crewai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
434
- blaxel/crewai/tools.py,sha256=HVMSRTVrc0ygiCCNkL6xa3i31IWwFlGKB6oC-6iWrK8,789
434
+ blaxel/crewai/tools.py,sha256=Slcy1BDwzdOSa8ZHia1gSkKoQuuTHtq4nK9BLpMdlDE,3678
435
435
  blaxel/googleadk/__init__.py,sha256=3rOLe32eQt2-YN4ktHdhDWc3TK_OW0FgidTIhZ8Nnm8,225
436
- blaxel/googleadk/model.py,sha256=iW2VqUnTZjGo4d9keiHVQbzs5B5KngvYYruskYfMTWk,3758
436
+ blaxel/googleadk/model.py,sha256=vhSX7vqFtgLgwnb8dgseCmR57gZel5PwHD7WBS3Na9Y,4647
437
437
  blaxel/googleadk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
438
- blaxel/googleadk/tools.py,sha256=S9yDK5EPR_FrXhPwgd3CBR2FjoHOuKbYzax4X2v_LBo,2238
438
+ blaxel/googleadk/tools.py,sha256=rAdFi3nH49Onss4RFFZz4uBuq1Mx4vlywsr04a5nSF4,3510
439
439
  blaxel/langgraph/__init__.py,sha256=lw9d7bl5TsYbemToCtus5P6XnhzR4SAcBWM-1Pffc_U,126
440
- blaxel/langgraph/model.py,sha256=XxzD9GAglsAZCRb2zN23ApjHxJDeItGetqnGcwPDi-A,8114
440
+ blaxel/langgraph/model.py,sha256=SdWwhfFeYNauRIbqzb_zGgjE4oCKoUKKuZbDq-RcoOs,8578
441
441
  blaxel/langgraph/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
442
- blaxel/langgraph/tools.py,sha256=sutGgsbU5ptpHrf_fjYJi1LgYe0QuW6WpfpdBlRbP_U,2841
443
- blaxel/langgraph/custom/gemini.py,sha256=ui_nnfA5eqz-_F_bI3Nc8RkL4644Tx_NlnVM5aTGyxM,54572
442
+ blaxel/langgraph/tools.py,sha256=_Gw77ilq9Rhq1pmNmrFr0JItcKhvkCV4yfymW5L_lWc,2946
443
+ blaxel/langgraph/custom/gemini.py,sha256=Dd5GiaCDNKFxMJqd1nR54SmXf7IkSe0gu5OszE7blvk,54976
444
444
  blaxel/livekit/__init__.py,sha256=byUwOJ6MHeSXOYXmaVoHnC3ZUmpkqJ55u5mQglXX-SQ,124
445
- blaxel/livekit/model.py,sha256=Qhl3EVa4Uum1IsT3nyMxJwXrZn2eDvCyj6V8RHOaJsQ,1687
445
+ blaxel/livekit/model.py,sha256=ciBLX92X9SIgDcebWguO-1P4cCHMDzRXCQWesv30hmg,2080
446
446
  blaxel/livekit/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
447
- blaxel/livekit/tools.py,sha256=QipxGDnKqma_ktzTUpKgzdp5x4bvpQnxEVoqSTBesu8,1053
447
+ blaxel/livekit/tools.py,sha256=2TkCkqWI1FyKR638T-B9TVWE8wNwLC0NNf4ecUERr3U,1202
448
448
  blaxel/llamaindex/__init__.py,sha256=iZ3QbZhlwKvP91ChcqSXVkpRrzurMxJoQfKdZFzE2AA,127
449
- blaxel/llamaindex/model.py,sha256=wDWEXn2oLCjdsJ9aBN6po6-LuiSuYa3-V6SRxBMTKG0,9405
449
+ blaxel/llamaindex/model.py,sha256=j0Im7BiIpu_DQes0xAIYZyiGUIsL9irsBiP88epeXBU,10038
450
450
  blaxel/llamaindex/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
451
- blaxel/llamaindex/tools.py,sha256=5wnPX45sgLduk9fPkhQ0rHRGK3Ekrye0MJNtMdw3GII,1024
451
+ blaxel/llamaindex/tools.py,sha256=RhAox8XT6o9FpDgXcK8Cq1hjJo-TFlgioCoVOlz_iK4,1142
452
452
  blaxel/llamaindex/custom/cohere.py,sha256=8Kfo5BjrND6EFDZXosHNrgrg98ktvxhYG48ztaWptps,18707
453
453
  blaxel/openai/__init__.py,sha256=YkizVtcYL2m9v-z5B1EReYVu9n9V-DCxJhSB2mvqOs0,123
454
- blaxel/openai/model.py,sha256=OalejbWJHlHhx_LUClP3FRwhiNyeyqME9RzEE41s8zs,1244
454
+ blaxel/openai/model.py,sha256=jauDHyKzAWmgjOv569F6pRGmUjxL1G9E2xGhjZhQeyQ,1587
455
455
  blaxel/openai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
456
- blaxel/openai/tools.py,sha256=NFiqs1S8Uz2PGtPzAAYjNz2RGCTu3zANndmRE8IsCXo,1936
456
+ blaxel/openai/tools.py,sha256=NWCrOpuJVulGegj8WI4dW48ScycPgfa6W2zrJtzZxI0,2411
457
457
  blaxel/pydantic/__init__.py,sha256=-UDZ5VSrlgfqErJuiuphcAFwesijzXxYLeyC7yHEDrE,128
458
- blaxel/pydantic/model.py,sha256=fa26oGzgT6ZxWK-cXG_c5urkMVxjE1patco2N4K4S04,6581
458
+ blaxel/pydantic/model.py,sha256=q_FRVZ047hOolckxyp0n5pCeneWu7HvfaFF5XmA5BC0,7001
459
459
  blaxel/pydantic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
460
- blaxel/pydantic/tools.py,sha256=5ua0Cr98-Hgkyhc92Tc6hzJrDRiNGSbAphmZQtgA5j0,1956
460
+ blaxel/pydantic/tools.py,sha256=thrSIBXeWVF3ogyfDRqK504rv2DHnK37GHI7Ze9TrM0,3091
461
461
  blaxel/pydantic/custom/gemini.py,sha256=DddpfkP75hPPHokXWCpdk6NZiqObK5KJFxL3TXRN8Kg,1051
462
462
  blaxel/telemetry/__init__.py,sha256=g5wNqaXAVdRmUe0cX7qKHN1cftF2MaHo_QCS4D-u8CQ,257
463
463
  blaxel/telemetry/exporters.py,sha256=PFQ38SQYDftSmR4cGcWPhdMKo4uic6pE9qv72j6q-TE,2232
@@ -472,7 +472,7 @@ blaxel/telemetry/instrumentation/map.py,sha256=PCzZJj39yiYVYJrxLBNP-NW-tjjYyTijw
472
472
  blaxel/telemetry/instrumentation/utils.py,sha256=FGyMY5ZE4f-0JdZpm_R_BCoKLJ18hftz8vsh7ftDwMk,1889
473
473
  blaxel/telemetry/log/log.py,sha256=vtzUIFIIj4MTTKUigILDYXN8NHHPOo44OaKukpyIjQg,2407
474
474
  blaxel/telemetry/log/logger.py,sha256=IcFWCd1yyWWGAjAd2i0pDYqpZHQ61pmcaQ7Kf4bC8lg,4150
475
- blaxel-0.2.38rc122.dist-info/METADATA,sha256=--Hscbw_W5MLB8BsePm4W1NIyMklqQuO-jdV0EBoI1E,18689
476
- blaxel-0.2.38rc122.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
477
- blaxel-0.2.38rc122.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
478
- blaxel-0.2.38rc122.dist-info/RECORD,,
475
+ blaxel-0.2.39.dist-info/METADATA,sha256=_mhXijtEXJloaPxYh4md_2i63SRdafVgWxYggj4s5_E,16109
476
+ blaxel-0.2.39.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
477
+ blaxel-0.2.39.dist-info/licenses/LICENSE,sha256=p5PNQvpvyDT_0aYBDgmV1fFI_vAD2aSV0wWG7VTgRis,1069
478
+ blaxel-0.2.39.dist-info/RECORD,,