chatlas 0.13.1__tar.gz → 0.13.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of chatlas might be problematic. Click here for more details.
- {chatlas-0.13.1 → chatlas-0.13.2}/CHANGELOG.md +10 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/PKG-INFO +4 -1
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_content.py +32 -2
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_databricks.py +7 -1
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_openai.py +7 -1
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_version.py +2 -2
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/anthropic/_client.py +2 -2
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/anthropic/_submit.py +10 -12
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/openai/_client.py +1 -1
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/openai/_submit.py +32 -32
- {chatlas-0.13.1 → chatlas-0.13.2}/pyproject.toml +4 -1
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_content_tools.py +92 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_azure.py +17 -9
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_databricks.py +10 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_tokens.py +1 -1
- {chatlas-0.13.1 → chatlas-0.13.2}/.github/workflows/check-update-types.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.github/workflows/docs-publish.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.github/workflows/release.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.github/workflows/test.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.github/workflows/update-pricing.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.gitignore +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.vscode/extensions.json +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/.vscode/settings.json +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/CLAUDE.md +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/LICENSE +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/Makefile +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/README.md +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/__init__.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_auto.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_batch_chat.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_batch_job.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_callbacks.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_chat.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_content_image.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_content_pdf.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_display.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_interpolate.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_live_render.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_logging.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_mcp_manager.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_merge.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_anthropic.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_cloudflare.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_deepseek.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_github.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_google.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_groq.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_huggingface.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_mistral.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_ollama.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_openrouter.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_perplexity.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_portkey.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_provider_snowflake.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_tokens.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_tokens_old.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_tools.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_turn.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_typing_extensions.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/_utils.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/data/prices.json +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/py.typed +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/__init__.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/anthropic/__init__.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/anthropic/_client_bedrock.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/google/__init__.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/google/_client.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/google/_submit.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/openai/__init__.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/chatlas/types/openai/_client_azure.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/.gitignore +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/_extensions/machow/interlinks/.gitignore +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/_extensions/machow/interlinks/_extension.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/_extensions/machow/interlinks/interlinks.lua +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/_quarto.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/_sidebar.yml +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/congressional-assets.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/async.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/chat.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/chatbots.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/debug.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/models.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/monitor.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/parameters.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/stream.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/structured-data.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/system-prompt.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/get-started/tools.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chat-app.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chat-console.mp4 +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chat-console.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chat-notebook.mp4 +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chat-parameters.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chatbot-gradio.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chatbot-shiny.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chatbot-streamlit.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chatbot-textual.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/chatlas-hello.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/client-parameters.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/congressional-assets.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/hello-chat-console.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/model-parameters.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/model-type-hints.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/posit-logo.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/shiny-mcp-run-python.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/shiny-tool-call-display.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/shiny-tool-call-map.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/tool-calling-right.svg +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/images/tool-calling-wrong.svg +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/index.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/logos/hero/hero-old.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/logos/hero/hero.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/logos/hex/logo.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/logos/small/logo.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/misc/RAG.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/misc/examples.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/misc/mcp-tools.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/misc/vocabulary.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/structured-data/article-summary.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/structured-data/classification.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/structured-data/entity-recognition.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/structured-data/multi-modal.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/structured-data/sentiment-analysis.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/styles.scss +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/tool-calling/approval.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/tool-calling/displays.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/tool-calling/how-it-works.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/docs/why-chatlas.qmd +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/pytest.ini +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/scripts/_generate_anthropic_types.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/scripts/_generate_google_types.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/scripts/_generate_openai_types.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/scripts/_utils.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/scripts/main.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/__init__.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/__snapshots__/test_chat.ambr +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/apples.pdf +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/batch/country-capitals-structured.json +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/batch/country-capitals.json +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/conftest.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/images/dice.png +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/mcp_servers/http_add.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/mcp_servers/http_current_date.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/mcp_servers/stdio_current_date.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/mcp_servers/stdio_subtract_multiply.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_auto.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_batch_chat.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_callbacks.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_chat.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_content.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_content_html.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_content_image.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_content_pdf.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_interpolate.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_mcp_client.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_anthropic.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_bedrock.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_cloudflare.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_deepseek.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_github.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_google.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_huggingface.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_mistral.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_openai.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_openrouter.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_portkey.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_provider_snowflake.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_register_tool_models.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_set_model_params.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_tool_from_mcp.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_tools_enhanced.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_turns.py +0 -0
- {chatlas-0.13.1 → chatlas-0.13.2}/tests/test_utils_merge.py +0 -0
|
@@ -7,6 +7,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
-->
|
|
9
9
|
|
|
10
|
+
## [0.13.2] - 2025-10-02
|
|
11
|
+
|
|
12
|
+
### Improvements
|
|
13
|
+
|
|
14
|
+
* `ContentToolResult`'s `.get_model_value()` method now calls `.to_json(orient="record")` (instead of `.to_json()`) when relevant. As a result, if a tool call returns a Pandas `DataFrame` (or similar), the model now receives a less confusing (and smaller) JSON format. (#183)
|
|
15
|
+
|
|
16
|
+
### Bug fixes
|
|
17
|
+
|
|
18
|
+
* `ChatAzureOpenAI()` and `ChatDatabricks()` now work as expected when a `OPENAI_API_KEY` environment variable isn't present. (#185)
|
|
19
|
+
|
|
10
20
|
## [0.13.1] - 2025-09-18
|
|
11
21
|
|
|
12
22
|
### Bug fixes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chatlas
|
|
3
|
-
Version: 0.13.
|
|
3
|
+
Version: 0.13.2
|
|
4
4
|
Summary: A simple and consistent interface for chatting with LLMs
|
|
5
5
|
Project-URL: Homepage, https://posit-dev.github.io/chatlas
|
|
6
6
|
Project-URL: Documentation, https://posit-dev.github.io/chatlas
|
|
@@ -38,9 +38,12 @@ Requires-Dist: databricks-sdk; extra == 'dev'
|
|
|
38
38
|
Requires-Dist: google-genai>=1.14.0; extra == 'dev'
|
|
39
39
|
Requires-Dist: htmltools; extra == 'dev'
|
|
40
40
|
Requires-Dist: matplotlib; extra == 'dev'
|
|
41
|
+
Requires-Dist: narwhals; extra == 'dev'
|
|
41
42
|
Requires-Dist: numpy>1.24.4; extra == 'dev'
|
|
42
43
|
Requires-Dist: openai; extra == 'dev'
|
|
44
|
+
Requires-Dist: pandas; extra == 'dev'
|
|
43
45
|
Requires-Dist: pillow; extra == 'dev'
|
|
46
|
+
Requires-Dist: polars; extra == 'dev'
|
|
44
47
|
Requires-Dist: python-dotenv; extra == 'dev'
|
|
45
48
|
Requires-Dist: ruff>=0.6.5; extra == 'dev'
|
|
46
49
|
Requires-Dist: shiny; extra == 'dev'
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import inspect
|
|
4
|
+
import warnings
|
|
3
5
|
from pprint import pformat
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast
|
|
5
7
|
|
|
6
8
|
import orjson
|
|
7
9
|
from pydantic import BaseModel, ConfigDict
|
|
@@ -465,8 +467,36 @@ class ContentToolResult(Content):
|
|
|
465
467
|
|
|
466
468
|
@staticmethod
|
|
467
469
|
def _to_json(value: Any) -> object:
|
|
470
|
+
if hasattr(value, "to_pandas") and callable(value.to_pandas):
|
|
471
|
+
# Many (most?) df libs (polars, pyarrow, ...) have a .to_pandas()
|
|
472
|
+
# method, and pandas has a .to_json() method
|
|
473
|
+
value = value.to_pandas()
|
|
474
|
+
|
|
468
475
|
if hasattr(value, "to_json") and callable(value.to_json):
|
|
469
|
-
|
|
476
|
+
# pandas defaults to "columns", which is not ideal for LLMs
|
|
477
|
+
# https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.to_json.html
|
|
478
|
+
sig = inspect.signature(value.to_json)
|
|
479
|
+
if "orient" in list(sig.parameters.keys()):
|
|
480
|
+
return value.to_json(orient="records")
|
|
481
|
+
else:
|
|
482
|
+
return value.to_json()
|
|
483
|
+
|
|
484
|
+
# Support for df libs (beyond those with a .to_pandas() method)
|
|
485
|
+
if hasattr(value, "__narwhals_dataframe__"):
|
|
486
|
+
try:
|
|
487
|
+
import narwhals
|
|
488
|
+
|
|
489
|
+
val = cast(narwhals.DataFrame, narwhals.from_native(value))
|
|
490
|
+
return val.to_pandas().to_json(orient="records")
|
|
491
|
+
except ImportError:
|
|
492
|
+
warnings.warn(
|
|
493
|
+
f"Tool result object of type {type(value)} appears to be a "
|
|
494
|
+
"narwhals-compatible DataFrame. If you run into issues with "
|
|
495
|
+
"the LLM not understanding this value, try installing narwhals: "
|
|
496
|
+
"`pip install narwhals`.",
|
|
497
|
+
ImportWarning,
|
|
498
|
+
stacklevel=2,
|
|
499
|
+
)
|
|
470
500
|
|
|
471
501
|
if hasattr(value, "to_dict") and callable(value.to_dict):
|
|
472
502
|
value = value.to_dict()
|
|
@@ -106,7 +106,13 @@ class DatabricksProvider(OpenAIProvider):
|
|
|
106
106
|
import httpx
|
|
107
107
|
from openai import AsyncOpenAI
|
|
108
108
|
|
|
109
|
-
super().__init__(
|
|
109
|
+
super().__init__(
|
|
110
|
+
name=name,
|
|
111
|
+
model=model,
|
|
112
|
+
# The OpenAI() constructor will fail if no API key is present.
|
|
113
|
+
# However, a dummy value is fine -- WorkspaceClient() handles the auth.
|
|
114
|
+
api_key="not-used",
|
|
115
|
+
)
|
|
110
116
|
|
|
111
117
|
self._seed = None
|
|
112
118
|
|
|
@@ -884,7 +884,13 @@ class OpenAIAzureProvider(OpenAIProvider):
|
|
|
884
884
|
model: Optional[str] = "UnusedValue",
|
|
885
885
|
kwargs: Optional["ChatAzureClientArgs"] = None,
|
|
886
886
|
):
|
|
887
|
-
super().__init__(
|
|
887
|
+
super().__init__(
|
|
888
|
+
name=name,
|
|
889
|
+
model=deployment_id,
|
|
890
|
+
# The OpenAI() constructor will fail if no API key is present.
|
|
891
|
+
# However, a dummy value is fine -- AzureOpenAI() handles the auth.
|
|
892
|
+
api_key=api_key or "not-used",
|
|
893
|
+
)
|
|
888
894
|
|
|
889
895
|
self._seed = seed
|
|
890
896
|
|
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '0.13.
|
|
32
|
-
__version_tuple__ = version_tuple = (0, 13,
|
|
31
|
+
__version__ = version = '0.13.2'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 13, 2)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# ---------------------------------------------------------
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
from typing import Mapping, Optional, TypedDict
|
|
6
|
+
from typing import Mapping, Optional, TypedDict
|
|
7
7
|
|
|
8
8
|
import anthropic
|
|
9
9
|
import httpx
|
|
@@ -13,7 +13,7 @@ class ChatClientArgs(TypedDict, total=False):
|
|
|
13
13
|
api_key: str | None
|
|
14
14
|
auth_token: str | None
|
|
15
15
|
base_url: str | httpx.URL | None
|
|
16
|
-
timeout:
|
|
16
|
+
timeout: float | anthropic.Timeout | None | anthropic.NotGiven
|
|
17
17
|
max_retries: int
|
|
18
18
|
default_headers: Optional[Mapping[str, str]]
|
|
19
19
|
default_query: Optional[Mapping[str, object]]
|
|
@@ -47,26 +47,24 @@ class SubmitInputArgs(TypedDict, total=False):
|
|
|
47
47
|
],
|
|
48
48
|
str,
|
|
49
49
|
]
|
|
50
|
-
service_tier: Union[Literal["auto", "standard_only"], anthropic.
|
|
51
|
-
stop_sequences: Union[Sequence[str], anthropic.
|
|
52
|
-
stream: Union[Literal[False], Literal[True], anthropic.
|
|
50
|
+
service_tier: Union[Literal["auto", "standard_only"], anthropic.Omit]
|
|
51
|
+
stop_sequences: Union[Sequence[str], anthropic.Omit]
|
|
52
|
+
stream: Union[Literal[False], Literal[True], anthropic.Omit]
|
|
53
53
|
system: Union[
|
|
54
|
-
str,
|
|
55
|
-
Iterable[anthropic.types.text_block_param.TextBlockParam],
|
|
56
|
-
anthropic.NotGiven,
|
|
54
|
+
str, Iterable[anthropic.types.text_block_param.TextBlockParam], anthropic.Omit
|
|
57
55
|
]
|
|
58
|
-
temperature: float | anthropic.
|
|
56
|
+
temperature: float | anthropic.Omit
|
|
59
57
|
thinking: Union[
|
|
60
58
|
anthropic.types.thinking_config_enabled_param.ThinkingConfigEnabledParam,
|
|
61
59
|
anthropic.types.thinking_config_disabled_param.ThinkingConfigDisabledParam,
|
|
62
|
-
anthropic.
|
|
60
|
+
anthropic.Omit,
|
|
63
61
|
]
|
|
64
62
|
tool_choice: Union[
|
|
65
63
|
anthropic.types.tool_choice_auto_param.ToolChoiceAutoParam,
|
|
66
64
|
anthropic.types.tool_choice_any_param.ToolChoiceAnyParam,
|
|
67
65
|
anthropic.types.tool_choice_tool_param.ToolChoiceToolParam,
|
|
68
66
|
anthropic.types.tool_choice_none_param.ToolChoiceNoneParam,
|
|
69
|
-
anthropic.
|
|
67
|
+
anthropic.Omit,
|
|
70
68
|
]
|
|
71
69
|
tools: Union[
|
|
72
70
|
Iterable[
|
|
@@ -79,10 +77,10 @@ class SubmitInputArgs(TypedDict, total=False):
|
|
|
79
77
|
anthropic.types.web_search_tool_20250305_param.WebSearchTool20250305Param,
|
|
80
78
|
]
|
|
81
79
|
],
|
|
82
|
-
anthropic.
|
|
80
|
+
anthropic.Omit,
|
|
83
81
|
]
|
|
84
|
-
top_k: int | anthropic.
|
|
85
|
-
top_p: float | anthropic.
|
|
82
|
+
top_k: int | anthropic.Omit
|
|
83
|
+
top_p: float | anthropic.Omit
|
|
86
84
|
extra_headers: Optional[Mapping[str, Union[str, anthropic.Omit]]]
|
|
87
85
|
extra_query: Optional[Mapping[str, object]]
|
|
88
86
|
extra_body: object | None
|
|
@@ -16,7 +16,7 @@ class ChatClientArgs(TypedDict, total=False):
|
|
|
16
16
|
webhook_secret: str | None
|
|
17
17
|
base_url: str | httpx.URL | None
|
|
18
18
|
websocket_base_url: str | httpx.URL | None
|
|
19
|
-
timeout:
|
|
19
|
+
timeout: float | openai.Timeout | None | openai.NotGiven
|
|
20
20
|
max_retries: int
|
|
21
21
|
default_headers: Optional[Mapping[str, str]]
|
|
22
22
|
default_query: Optional[Mapping[str, object]]
|
|
@@ -108,61 +108,61 @@ class SubmitInputArgs(TypedDict, total=False):
|
|
|
108
108
|
audio: Union[
|
|
109
109
|
openai.types.chat.chat_completion_audio_param.ChatCompletionAudioParam,
|
|
110
110
|
None,
|
|
111
|
-
openai.
|
|
111
|
+
openai.Omit,
|
|
112
112
|
]
|
|
113
|
-
frequency_penalty: Union[float, None, openai.
|
|
113
|
+
frequency_penalty: Union[float, None, openai.Omit]
|
|
114
114
|
function_call: Union[
|
|
115
115
|
Literal["none", "auto"],
|
|
116
116
|
openai.types.chat.chat_completion_function_call_option_param.ChatCompletionFunctionCallOptionParam,
|
|
117
|
-
openai.
|
|
117
|
+
openai.Omit,
|
|
118
118
|
]
|
|
119
119
|
functions: Union[
|
|
120
|
-
Iterable[openai.types.chat.completion_create_params.Function], openai.
|
|
120
|
+
Iterable[openai.types.chat.completion_create_params.Function], openai.Omit
|
|
121
121
|
]
|
|
122
|
-
logit_bias: Union[dict[str, int], None, openai.
|
|
123
|
-
logprobs: Union[bool, None, openai.
|
|
124
|
-
max_completion_tokens: Union[int, None, openai.
|
|
125
|
-
max_tokens: Union[int, None, openai.
|
|
126
|
-
metadata: Union[dict[str, str], None, openai.
|
|
127
|
-
modalities: Union[list[Literal["text", "audio"]], None, openai.
|
|
128
|
-
n: Union[int, None, openai.
|
|
129
|
-
parallel_tool_calls: bool | openai.
|
|
122
|
+
logit_bias: Union[dict[str, int], None, openai.Omit]
|
|
123
|
+
logprobs: Union[bool, None, openai.Omit]
|
|
124
|
+
max_completion_tokens: Union[int, None, openai.Omit]
|
|
125
|
+
max_tokens: Union[int, None, openai.Omit]
|
|
126
|
+
metadata: Union[dict[str, str], None, openai.Omit]
|
|
127
|
+
modalities: Union[list[Literal["text", "audio"]], None, openai.Omit]
|
|
128
|
+
n: Union[int, None, openai.Omit]
|
|
129
|
+
parallel_tool_calls: bool | openai.Omit
|
|
130
130
|
prediction: Union[
|
|
131
131
|
openai.types.chat.chat_completion_prediction_content_param.ChatCompletionPredictionContentParam,
|
|
132
132
|
None,
|
|
133
|
-
openai.
|
|
133
|
+
openai.Omit,
|
|
134
134
|
]
|
|
135
|
-
presence_penalty: Union[float, None, openai.
|
|
136
|
-
prompt_cache_key: str | openai.
|
|
135
|
+
presence_penalty: Union[float, None, openai.Omit]
|
|
136
|
+
prompt_cache_key: str | openai.Omit
|
|
137
137
|
reasoning_effort: Union[
|
|
138
|
-
Literal["minimal", "low", "medium", "high"], None, openai.
|
|
138
|
+
Literal["minimal", "low", "medium", "high"], None, openai.Omit
|
|
139
139
|
]
|
|
140
140
|
response_format: Union[
|
|
141
141
|
openai.types.shared_params.response_format_text.ResponseFormatText,
|
|
142
142
|
openai.types.shared_params.response_format_json_schema.ResponseFormatJSONSchema,
|
|
143
143
|
openai.types.shared_params.response_format_json_object.ResponseFormatJSONObject,
|
|
144
|
-
openai.
|
|
144
|
+
openai.Omit,
|
|
145
145
|
]
|
|
146
|
-
safety_identifier: str | openai.
|
|
147
|
-
seed: Union[int, None, openai.
|
|
146
|
+
safety_identifier: str | openai.Omit
|
|
147
|
+
seed: Union[int, None, openai.Omit]
|
|
148
148
|
service_tier: Union[
|
|
149
|
-
Literal["auto", "default", "flex", "scale", "priority"], None, openai.
|
|
149
|
+
Literal["auto", "default", "flex", "scale", "priority"], None, openai.Omit
|
|
150
150
|
]
|
|
151
|
-
stop: Union[str, None, Sequence[str], openai.
|
|
152
|
-
store: Union[bool, None, openai.
|
|
153
|
-
stream: Union[Literal[False], None, Literal[True], openai.
|
|
151
|
+
stop: Union[str, None, Sequence[str], openai.Omit]
|
|
152
|
+
store: Union[bool, None, openai.Omit]
|
|
153
|
+
stream: Union[Literal[False], None, Literal[True], openai.Omit]
|
|
154
154
|
stream_options: Union[
|
|
155
155
|
openai.types.chat.chat_completion_stream_options_param.ChatCompletionStreamOptionsParam,
|
|
156
156
|
None,
|
|
157
|
-
openai.
|
|
157
|
+
openai.Omit,
|
|
158
158
|
]
|
|
159
|
-
temperature: Union[float, None, openai.
|
|
159
|
+
temperature: Union[float, None, openai.Omit]
|
|
160
160
|
tool_choice: Union[
|
|
161
161
|
Literal["none", "auto", "required"],
|
|
162
162
|
openai.types.chat.chat_completion_allowed_tool_choice_param.ChatCompletionAllowedToolChoiceParam,
|
|
163
163
|
openai.types.chat.chat_completion_named_tool_choice_param.ChatCompletionNamedToolChoiceParam,
|
|
164
164
|
openai.types.chat.chat_completion_named_tool_choice_custom_param.ChatCompletionNamedToolChoiceCustomParam,
|
|
165
|
-
openai.
|
|
165
|
+
openai.Omit,
|
|
166
166
|
]
|
|
167
167
|
tools: Union[
|
|
168
168
|
Iterable[
|
|
@@ -171,14 +171,14 @@ class SubmitInputArgs(TypedDict, total=False):
|
|
|
171
171
|
openai.types.chat.chat_completion_custom_tool_param.ChatCompletionCustomToolParam,
|
|
172
172
|
]
|
|
173
173
|
],
|
|
174
|
-
openai.
|
|
174
|
+
openai.Omit,
|
|
175
175
|
]
|
|
176
|
-
top_logprobs: Union[int, None, openai.
|
|
177
|
-
top_p: Union[float, None, openai.
|
|
178
|
-
user: str | openai.
|
|
179
|
-
verbosity: Union[Literal["low", "medium", "high"], None, openai.
|
|
176
|
+
top_logprobs: Union[int, None, openai.Omit]
|
|
177
|
+
top_p: Union[float, None, openai.Omit]
|
|
178
|
+
user: str | openai.Omit
|
|
179
|
+
verbosity: Union[Literal["low", "medium", "high"], None, openai.Omit]
|
|
180
180
|
web_search_options: (
|
|
181
|
-
openai.types.chat.completion_create_params.WebSearchOptions | openai.
|
|
181
|
+
openai.types.chat.completion_create_params.WebSearchOptions | openai.Omit
|
|
182
182
|
)
|
|
183
183
|
extra_headers: Optional[Mapping[str, Union[str, openai.Omit]]]
|
|
184
184
|
extra_query: Optional[Mapping[str, object]]
|
|
@@ -48,7 +48,11 @@ dev = [
|
|
|
48
48
|
"matplotlib",
|
|
49
49
|
"Pillow",
|
|
50
50
|
"shiny",
|
|
51
|
+
"htmltools",
|
|
51
52
|
"shinychat",
|
|
53
|
+
"narwhals",
|
|
54
|
+
"pandas",
|
|
55
|
+
"polars",
|
|
52
56
|
"openai",
|
|
53
57
|
"anthropic[bedrock]",
|
|
54
58
|
"google-genai>=1.14.0",
|
|
@@ -58,7 +62,6 @@ dev = [
|
|
|
58
62
|
"snowflake-ml-python>=1.8.4",
|
|
59
63
|
# torch (a dependency of snowflake-ml-python) is not yet compatible with Python >3.11
|
|
60
64
|
"torch;python_version<='3.11'",
|
|
61
|
-
"htmltools",
|
|
62
65
|
"tenacity"
|
|
63
66
|
]
|
|
64
67
|
docs = [
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import Any, Optional, Union
|
|
2
|
+
from unittest.mock import Mock
|
|
2
3
|
|
|
4
|
+
import orjson
|
|
3
5
|
import pytest
|
|
4
6
|
|
|
5
7
|
from chatlas import ChatOpenAI
|
|
@@ -403,3 +405,93 @@ def test_content_tool_request_serializable():
|
|
|
403
405
|
assert parsed.tool is not None
|
|
404
406
|
assert parsed.tool.name == "add"
|
|
405
407
|
assert parsed.tool.description == "Add two numbers"
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def test_content_tool_result_pandas_dataframe():
|
|
411
|
+
"""Test ContentToolResult with pandas DataFrame using orient='records'"""
|
|
412
|
+
pandas = pytest.importorskip("pandas")
|
|
413
|
+
|
|
414
|
+
# Create a simple pandas DataFrame
|
|
415
|
+
df = pandas.DataFrame(
|
|
416
|
+
{"name": ["Alice", "Bob"], "age": [25, 30], "city": ["New York", "London"]}
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
# Create ContentToolResult with DataFrame value
|
|
420
|
+
result = ContentToolResult(value=df).get_model_value()
|
|
421
|
+
expected = df.to_json(orient="records")
|
|
422
|
+
assert result == expected
|
|
423
|
+
|
|
424
|
+
parsed = orjson.loads(str(result))
|
|
425
|
+
assert isinstance(parsed, list)
|
|
426
|
+
assert len(parsed) == 2
|
|
427
|
+
assert parsed[0] == {"name": "Alice", "age": 25, "city": "New York"}
|
|
428
|
+
assert parsed[1] == {"name": "Bob", "age": 30, "city": "London"}
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def test_content_tool_result_object_with_to_pandas():
|
|
432
|
+
"""Test ContentToolResult with objects that have .to_pandas() method"""
|
|
433
|
+
pandas = pytest.importorskip("pandas")
|
|
434
|
+
|
|
435
|
+
# Create mock object with to_pandas method (like Polars, PyArrow)
|
|
436
|
+
mock_df_lib = Mock()
|
|
437
|
+
pandas_df = pandas.DataFrame({"x": [1, 2, 3], "y": ["a", "b", "c"]})
|
|
438
|
+
mock_df_lib.to_pandas.return_value = pandas_df
|
|
439
|
+
|
|
440
|
+
result = ContentToolResult(value=mock_df_lib).get_model_value()
|
|
441
|
+
mock_df_lib.to_pandas.assert_called_once()
|
|
442
|
+
expected = pandas_df.to_json(orient="records")
|
|
443
|
+
assert result == expected
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def test_content_tool_result_narwhals_dataframe():
|
|
447
|
+
"""Test ContentToolResult with narwhals DataFrame"""
|
|
448
|
+
narwhals = pytest.importorskip("narwhals")
|
|
449
|
+
pandas = pytest.importorskip("pandas")
|
|
450
|
+
|
|
451
|
+
pandas_df = pandas.DataFrame({"a": [1, 2], "b": ["x", "y"]})
|
|
452
|
+
nw_df = narwhals.from_native(pandas_df)
|
|
453
|
+
result = ContentToolResult(value=nw_df).get_model_value()
|
|
454
|
+
expected = pandas_df.to_json(orient="records")
|
|
455
|
+
assert result == expected
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
def test_content_tool_result_object_with_to_dict():
|
|
459
|
+
"""Test ContentToolResult with objects that have to_dict method"""
|
|
460
|
+
# Mock object with to_dict method but no to_pandas or to_json
|
|
461
|
+
mock_obj = Mock(spec=["to_dict"])
|
|
462
|
+
mock_obj.to_dict.return_value = {"key": "value"}
|
|
463
|
+
result = ContentToolResult(value=mock_obj).get_model_value()
|
|
464
|
+
mock_obj.to_dict.assert_called_once()
|
|
465
|
+
# Result should be JSON string representation (orjson format)
|
|
466
|
+
assert result == '{"key":"value"}'
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
def test_content_tool_result_string_passthrough():
|
|
470
|
+
"""Test ContentToolResult with string values (special case - passed through as-is)"""
|
|
471
|
+
result = ContentToolResult(value="plain string").get_model_value()
|
|
472
|
+
assert result == "plain string"
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def test_content_tool_result_fallback_serialization():
|
|
476
|
+
"""Test ContentToolResult fallback for objects without special methods"""
|
|
477
|
+
# Regular object without to_json, to_pandas, or to_dict (non-string to avoid the string special case)
|
|
478
|
+
result = ContentToolResult(value={"key": "value"}).get_model_value()
|
|
479
|
+
assert result == '{"key":"value"}'
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
def test_content_tool_result_explicit_json_mode():
|
|
483
|
+
"""Test ContentToolResult with explicit JSON mode forces _to_json for non-strings"""
|
|
484
|
+
# Test with non-string object and explicit JSON mode
|
|
485
|
+
result = ContentToolResult(
|
|
486
|
+
value={"key": "value"},
|
|
487
|
+
model_format="json",
|
|
488
|
+
).get_model_value()
|
|
489
|
+
# With explicit JSON mode, objects get JSON-encoded
|
|
490
|
+
assert result == '{"key":"value"}'
|
|
491
|
+
# Test that strings still get special treatment even in JSON mode
|
|
492
|
+
string_result = ContentToolResult(
|
|
493
|
+
value="plain string",
|
|
494
|
+
model_format="json",
|
|
495
|
+
).get_model_value()
|
|
496
|
+
# Strings are still returned as-is even in JSON mode (current behavior)
|
|
497
|
+
assert string_result == "plain string"
|
|
@@ -9,14 +9,17 @@ if do_test.lower() == "false":
|
|
|
9
9
|
pytest.skip("Skipping Azure tests", allow_module_level=True)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def
|
|
13
|
-
|
|
14
|
-
system_prompt=
|
|
12
|
+
def chat_func(system_prompt: str = "Be as terse as possible; no punctuation"):
|
|
13
|
+
return ChatAzureOpenAI(
|
|
14
|
+
system_prompt=system_prompt,
|
|
15
15
|
endpoint="https://chatlas-testing.openai.azure.com",
|
|
16
16
|
deployment_id="gpt-4o-mini",
|
|
17
17
|
api_version="2024-08-01-preview",
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
+
|
|
21
|
+
def test_azure_simple_request():
|
|
22
|
+
chat = chat_func()
|
|
20
23
|
response = chat.chat("What is 1 + 1?")
|
|
21
24
|
assert "2" == response.get_content()
|
|
22
25
|
turn = chat.get_last_turn()
|
|
@@ -27,15 +30,20 @@ def test_azure_simple_request():
|
|
|
27
30
|
|
|
28
31
|
@pytest.mark.asyncio
|
|
29
32
|
async def test_azure_simple_request_async():
|
|
30
|
-
chat =
|
|
31
|
-
system_prompt="Be as terse as possible; no punctuation",
|
|
32
|
-
endpoint="https://chatlas-testing.openai.azure.com",
|
|
33
|
-
deployment_id="gpt-4o-mini",
|
|
34
|
-
api_version="2024-08-01-preview",
|
|
35
|
-
)
|
|
33
|
+
chat = chat_func()
|
|
36
34
|
|
|
37
35
|
response = await chat.chat_async("What is 1 + 1?")
|
|
38
36
|
assert "2" == await response.get_content()
|
|
39
37
|
turn = chat.get_last_turn()
|
|
40
38
|
assert turn is not None
|
|
41
39
|
assert turn.tokens == (27, 2, 0)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_connect_without_openai_key(monkeypatch):
|
|
43
|
+
# Ensure OPENAI_API_KEY is not set
|
|
44
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
45
|
+
|
|
46
|
+
# This should not raise an error
|
|
47
|
+
chat = chat_func()
|
|
48
|
+
assert chat is not None
|
|
49
|
+
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import pytest
|
|
2
|
+
|
|
2
3
|
from chatlas import ChatDatabricks
|
|
3
4
|
|
|
4
5
|
from .conftest import assert_turns_existing, assert_turns_system
|
|
@@ -82,3 +83,12 @@ def test_anthropic_empty_response():
|
|
|
82
83
|
# def test_openai_pdf():
|
|
83
84
|
# chat_fun = ChatDatabricks
|
|
84
85
|
# assert_pdf_local(chat_fun)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_connect_without_openai_key(monkeypatch):
|
|
89
|
+
# Ensure OPENAI_API_KEY is not set
|
|
90
|
+
monkeypatch.delenv("OPENAI_API_KEY", raising=False)
|
|
91
|
+
|
|
92
|
+
# This should not raise an error
|
|
93
|
+
chat = ChatDatabricks()
|
|
94
|
+
assert chat is not None
|
|
@@ -70,7 +70,7 @@ def test_token_count_method():
|
|
|
70
70
|
chat = ChatAnthropic(model="claude-3-5-sonnet-20241022")
|
|
71
71
|
assert chat.token_count("What is 1 + 1?") == 16
|
|
72
72
|
|
|
73
|
-
chat = ChatGoogle(model="gemini-
|
|
73
|
+
chat = ChatGoogle(model="gemini-2.5-flash")
|
|
74
74
|
assert chat.token_count("What is 1 + 1?") == 9
|
|
75
75
|
|
|
76
76
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|