chatlas 0.11.1__py3-none-any.whl → 0.13.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of chatlas might be problematic. Click here for more details.

chatlas/_provider.py CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  from abc import ABC, abstractmethod
4
4
  from datetime import date
5
5
  from typing import (
6
+ Any,
6
7
  AsyncIterable,
7
8
  Generic,
8
9
  Iterable,
@@ -100,6 +101,16 @@ StandardModelParamNames = Literal[
100
101
  ]
101
102
 
102
103
 
104
+ # Provider-agnostic batch status info
105
+ class BatchStatus(BaseModel):
106
+ """Status information for a batch job."""
107
+
108
+ working: bool
109
+ n_processing: int
110
+ n_succeeded: int
111
+ n_failed: int
112
+
113
+
103
114
  class Provider(
104
115
  ABC,
105
116
  Generic[
@@ -261,3 +272,80 @@ class Provider(
261
272
 
262
273
  @abstractmethod
263
274
  def supported_model_params(self) -> set[StandardModelParamNames]: ...
275
+
276
+ def has_batch_support(self) -> bool:
277
+ """
278
+ Returns whether this provider supports batch processing.
279
+ Override this method to return True for providers that implement batch methods.
280
+ """
281
+ return False
282
+
283
+ def batch_submit(
284
+ self,
285
+ conversations: list[list[Turn]],
286
+ data_model: Optional[type[BaseModel]] = None,
287
+ ) -> dict[str, Any]:
288
+ """
289
+ Submit a batch of conversations for processing.
290
+
291
+ Args:
292
+ conversations: List of conversation histories (each is a list of Turns)
293
+ data_model: Optional structured data model for responses
294
+
295
+ Returns:
296
+ BatchInfo containing batch job information
297
+ """
298
+ raise NotImplementedError("This provider does not support batch processing")
299
+
300
+ def batch_poll(self, batch: dict[str, Any]) -> dict[str, Any]:
301
+ """
302
+ Poll the status of a submitted batch.
303
+
304
+ Args:
305
+ batch: Batch information returned from batch_submit
306
+
307
+ Returns:
308
+ Updated batch information
309
+ """
310
+ raise NotImplementedError("This provider does not support batch processing")
311
+
312
+ def batch_status(self, batch: dict[str, Any]) -> BatchStatus:
313
+ """
314
+ Get the status of a batch.
315
+
316
+ Args:
317
+ batch: Batch information
318
+
319
+ Returns:
320
+ BatchStatus with processing status information
321
+ """
322
+ raise NotImplementedError("This provider does not support batch processing")
323
+
324
+ def batch_retrieve(self, batch: dict[str, Any]) -> list[dict[str, Any]]:
325
+ """
326
+ Retrieve results from a completed batch.
327
+
328
+ Args:
329
+ batch: Batch information
330
+
331
+ Returns:
332
+ List of BatchResult objects, one for each request in the batch
333
+ """
334
+ raise NotImplementedError("This provider does not support batch processing")
335
+
336
+ def batch_result_turn(
337
+ self,
338
+ result: dict[str, Any],
339
+ has_data_model: bool = False,
340
+ ) -> Turn | None:
341
+ """
342
+ Convert a batch result to a Turn.
343
+
344
+ Args:
345
+ result: Individual BatchResult from batch_retrieve
346
+ has_data_model: Whether the request used a structured data model
347
+
348
+ Returns:
349
+ Turn object or None if the result was an error
350
+ """
351
+ raise NotImplementedError("This provider does not support batch processing")
@@ -1,10 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import base64
4
+ import re
4
5
  import warnings
5
6
  from typing import TYPE_CHECKING, Any, Literal, Optional, Union, cast, overload
6
7
 
7
8
  import orjson
9
+ from openai.types.chat import ChatCompletionToolParam
8
10
  from pydantic import BaseModel
9
11
 
10
12
  from ._chat import Chat
@@ -21,7 +23,13 @@ from ._content import (
21
23
  ContentToolResultResource,
22
24
  )
23
25
  from ._logging import log_model_default
24
- from ._provider import ModelInfo, Provider, StandardModelParamNames, StandardModelParams
26
+ from ._provider import (
27
+ BatchStatus,
28
+ ModelInfo,
29
+ Provider,
30
+ StandardModelParamNames,
31
+ StandardModelParams,
32
+ )
25
33
  from ._tokens import get_token_pricing, tokens_log
26
34
  from ._tools import Tool, basemodel_to_param_schema
27
35
  from ._turn import Turn, user_turn
@@ -38,11 +46,12 @@ if TYPE_CHECKING:
38
46
  )
39
47
  from anthropic.types.document_block_param import DocumentBlockParam
40
48
  from anthropic.types.image_block_param import ImageBlockParam
49
+ from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
50
+ from anthropic.types.messages.batch_create_params import Request as BatchRequest
41
51
  from anthropic.types.model_param import ModelParam
42
52
  from anthropic.types.text_block_param import TextBlockParam
43
53
  from anthropic.types.tool_result_block_param import ToolResultBlockParam
44
54
  from anthropic.types.tool_use_block_param import ToolUseBlockParam
45
- from openai.types.chat import ChatCompletionToolParam
46
55
 
47
56
  from .types.anthropic import ChatBedrockClientArgs, ChatClientArgs, SubmitInputArgs
48
57
 
@@ -631,6 +640,101 @@ class AnthropicProvider(
631
640
  completion=completion,
632
641
  )
633
642
 
643
+ def has_batch_support(self) -> bool:
644
+ return True
645
+
646
+ def batch_submit(
647
+ self,
648
+ conversations: list[list[Turn]],
649
+ data_model: Optional[type[BaseModel]] = None,
650
+ ):
651
+ from anthropic import NotGiven
652
+
653
+ requests: list["BatchRequest"] = []
654
+
655
+ for i, turns in enumerate(conversations):
656
+ kwargs = self._chat_perform_args(
657
+ stream=False,
658
+ turns=turns,
659
+ tools={},
660
+ data_model=data_model,
661
+ )
662
+
663
+ params: "MessageCreateParamsNonStreaming" = {
664
+ "messages": kwargs.get("messages", {}),
665
+ "model": self.model,
666
+ "max_tokens": kwargs.get("max_tokens", 4096),
667
+ }
668
+
669
+ # If data_model, tools/tool_choice should be present
670
+ tools = kwargs.get("tools")
671
+ tool_choice = kwargs.get("tool_choice")
672
+ if tools and not isinstance(tools, NotGiven):
673
+ params["tools"] = tools
674
+ if tool_choice and not isinstance(tool_choice, NotGiven):
675
+ params["tool_choice"] = tool_choice
676
+
677
+ requests.append({"custom_id": f"request-{i}", "params": params})
678
+
679
+ batch = self._client.messages.batches.create(requests=requests)
680
+ return batch.model_dump()
681
+
682
+ def batch_poll(self, batch):
683
+ from anthropic.types.messages import MessageBatch
684
+
685
+ batch = MessageBatch.model_validate(batch)
686
+ b = self._client.messages.batches.retrieve(batch.id)
687
+ return b.model_dump()
688
+
689
+ def batch_status(self, batch) -> "BatchStatus":
690
+ from anthropic.types.messages import MessageBatch
691
+
692
+ batch = MessageBatch.model_validate(batch)
693
+ status = batch.processing_status
694
+ counts = batch.request_counts
695
+
696
+ return BatchStatus(
697
+ working=status != "ended",
698
+ n_processing=counts.processing,
699
+ n_succeeded=counts.succeeded,
700
+ n_failed=counts.errored + counts.canceled + counts.expired,
701
+ )
702
+
703
+ # https://docs.anthropic.com/en/api/retrieving-message-batch-results
704
+ def batch_retrieve(self, batch):
705
+ from anthropic.types.messages import MessageBatch
706
+
707
+ batch = MessageBatch.model_validate(batch)
708
+ if batch.results_url is None:
709
+ raise ValueError("Batch has no results URL")
710
+
711
+ results: list[dict[str, Any]] = []
712
+ for res in self._client.messages.batches.results(batch.id):
713
+ results.append(res.model_dump())
714
+
715
+ # Sort by custom_id to maintain order
716
+ def extract_id(x: str):
717
+ match = re.search(r"-(\d+)$", x)
718
+ return int(match.group(1)) if match else 0
719
+
720
+ results.sort(key=lambda x: extract_id(x.get("custom_id", "")))
721
+
722
+ return results
723
+
724
+ def batch_result_turn(self, result, has_data_model: bool = False) -> Turn | None:
725
+ from anthropic.types.messages.message_batch_individual_response import (
726
+ MessageBatchIndividualResponse,
727
+ )
728
+
729
+ result = MessageBatchIndividualResponse.model_validate(result)
730
+ if result.result.type != "succeeded":
731
+ # TODO: offer advice on what to do?
732
+ warnings.warn(f"Batch request didn't succeed: {result.result}")
733
+ return None
734
+
735
+ message = result.result.message
736
+ return self._as_turn(message, has_data_model)
737
+
634
738
 
635
739
  def ChatBedrockAnthropic(
636
740
  *,
@@ -1,11 +1,18 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import base64
4
+ import json
5
+ import os
6
+ import re
7
+ import tempfile
8
+ import warnings
4
9
  from datetime import datetime
5
10
  from typing import TYPE_CHECKING, Any, Literal, Optional, cast, overload
6
11
 
7
12
  import orjson
8
13
  from openai import AsyncAzureOpenAI, AsyncOpenAI, AzureOpenAI, OpenAI
14
+ from openai.types.batch import Batch
15
+ from openai.types.chat import ChatCompletion, ChatCompletionChunk
9
16
  from pydantic import BaseModel
10
17
 
11
18
  from ._chat import Chat
@@ -24,18 +31,20 @@ from ._content import (
24
31
  )
25
32
  from ._logging import log_model_default
26
33
  from ._merge import merge_dicts
27
- from ._provider import ModelInfo, Provider, StandardModelParamNames, StandardModelParams
34
+ from ._provider import (
35
+ BatchStatus,
36
+ ModelInfo,
37
+ Provider,
38
+ StandardModelParamNames,
39
+ StandardModelParams,
40
+ )
28
41
  from ._tokens import get_token_pricing, tokens_log
29
42
  from ._tools import Tool, basemodel_to_param_schema
30
43
  from ._turn import Turn, user_turn
31
44
  from ._utils import MISSING, MISSING_TYPE, is_testing, split_http_client_kwargs
32
45
 
33
46
  if TYPE_CHECKING:
34
- from openai.types.chat import (
35
- ChatCompletion,
36
- ChatCompletionChunk,
37
- ChatCompletionMessageParam,
38
- )
47
+ from openai.types.chat import ChatCompletionMessageParam
39
48
  from openai.types.chat.chat_completion_assistant_message_param import (
40
49
  ContentArrayOfContentPart,
41
50
  )
@@ -45,10 +54,6 @@ if TYPE_CHECKING:
45
54
  from openai.types.chat_model import ChatModel
46
55
 
47
56
  from .types.openai import ChatAzureClientArgs, ChatClientArgs, SubmitInputArgs
48
- else:
49
- ChatCompletion = object
50
- ChatCompletionChunk = object
51
-
52
57
 
53
58
  # The dictionary form of ChatCompletion (TODO: stronger typing)?
54
59
  ChatCompletionDict = dict[str, Any]
@@ -171,6 +176,21 @@ def ChatOpenAI(
171
176
  )
172
177
 
173
178
 
179
+ # Seems there is no native typing support for `files.content()` results
180
+ # so mock them based on the docs here
181
+ # https://platform.openai.com/docs/guides/batch#5-retrieve-the-results
182
+ class BatchResult(BaseModel):
183
+ id: str
184
+ custom_id: str
185
+ response: BatchResultResponse
186
+
187
+
188
+ class BatchResultResponse(BaseModel):
189
+ status_code: int
190
+ request_id: str
191
+ body: ChatCompletionDict
192
+
193
+
174
194
  class OpenAIProvider(
175
195
  Provider[ChatCompletion, ChatCompletionChunk, ChatCompletionDict, "SubmitInputArgs"]
176
196
  ):
@@ -353,8 +373,6 @@ class OpenAIProvider(
353
373
  return merge_dicts(completion, chunkd)
354
374
 
355
375
  def stream_turn(self, completion, has_data_model) -> Turn:
356
- from openai.types.chat import ChatCompletion
357
-
358
376
  delta = completion["choices"][0].pop("delta") # type: ignore
359
377
  completion["choices"][0]["message"] = delta # type: ignore
360
378
  completion = ChatCompletion.construct(**completion)
@@ -662,6 +680,119 @@ class OpenAIProvider(
662
680
  "stop_sequences",
663
681
  }
664
682
 
683
+ def has_batch_support(self) -> bool:
684
+ return True
685
+
686
+ def batch_submit(
687
+ self,
688
+ conversations: list[list[Turn]],
689
+ data_model: Optional[type[BaseModel]] = None,
690
+ ):
691
+ # First put the requests in a file
692
+ # https://platform.openai.com/docs/api-reference/batch/request-input
693
+ # https://platform.openai.com/docs/api-reference/batch
694
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".jsonl", delete=False) as f:
695
+ temp_path = f.name
696
+
697
+ for i, turns in enumerate(conversations):
698
+ kwargs = self._chat_perform_args(
699
+ stream=False,
700
+ turns=turns,
701
+ tools={},
702
+ data_model=data_model,
703
+ )
704
+
705
+ body = {
706
+ "messages": kwargs.get("messages", []),
707
+ "model": self.model,
708
+ }
709
+
710
+ if "response_format" in kwargs:
711
+ body["response_format"] = kwargs["response_format"]
712
+
713
+ request = {
714
+ "custom_id": f"request-{i}",
715
+ "method": "POST",
716
+ "url": "/v1/chat/completions",
717
+ "body": body,
718
+ }
719
+
720
+ f.write(orjson.dumps(request).decode() + "\n")
721
+
722
+ try:
723
+ with open(temp_path, "rb") as f:
724
+ file_response = self._client.files.create(file=f, purpose="batch")
725
+
726
+ batch = self._client.batches.create(
727
+ input_file_id=file_response.id,
728
+ endpoint="/v1/chat/completions",
729
+ completion_window="24h",
730
+ )
731
+
732
+ return batch.model_dump()
733
+ finally:
734
+ os.unlink(temp_path)
735
+
736
+ def batch_poll(self, batch):
737
+ batch = Batch.model_validate(batch)
738
+ b = self._client.batches.retrieve(batch.id)
739
+ return b.model_dump()
740
+
741
+ def batch_status(self, batch):
742
+ batch = Batch.model_validate(batch)
743
+ counts = batch.request_counts
744
+ total, completed, failed = 0, 0, 0
745
+ if counts is not None:
746
+ total = counts.total
747
+ completed = counts.completed
748
+ failed = counts.failed
749
+
750
+ return BatchStatus(
751
+ working=batch.status not in ["completed", "failed", "cancelled"],
752
+ n_processing=total - completed - failed,
753
+ n_succeeded=completed,
754
+ n_failed=failed,
755
+ )
756
+
757
+ def batch_retrieve(self, batch):
758
+ batch = Batch.model_validate(batch)
759
+ if batch.output_file_id is None:
760
+ raise ValueError("Batch has no output file")
761
+
762
+ # Download and parse JSONL results
763
+ response = self._client.files.content(batch.output_file_id)
764
+ results: list[dict[str, Any]] = []
765
+ for line in response.text.splitlines():
766
+ results.append(json.loads(line))
767
+
768
+ # Sort by custom_id to maintain order
769
+ def extract_id(x: str):
770
+ match = re.search(r"-(\d+)$", x)
771
+ return int(match.group(1)) if match else 0
772
+
773
+ results.sort(key=lambda x: int(extract_id(x.get("custom_id", ""))))
774
+
775
+ return results
776
+
777
+ def batch_result_turn(
778
+ self,
779
+ result,
780
+ has_data_model: bool = False,
781
+ ) -> Turn | None:
782
+ response = BatchResult.model_validate(result).response
783
+ if response.status_code != 200:
784
+ # TODO: offer advice on what to do?
785
+ warnings.warn(f"Batch request failed: {response.body}")
786
+ return None
787
+
788
+ completion = ChatCompletion.construct(**response.body)
789
+ return self._as_turn(completion, has_data_model)
790
+
791
+
792
+ # -------------------------------------------------------------------------------------
793
+ # Azure OpenAI Chat
794
+ # -------------------------------------------------------------------------------------
795
+
665
796
 
666
797
  def ChatAzureOpenAI(
667
798
  *,
chatlas/_tools.py CHANGED
@@ -326,13 +326,21 @@ def func_to_basemodel(func: Callable) -> type[BaseModel]:
326
326
  )
327
327
  annotation = Any
328
328
 
329
+ # create_model() will error if the field name starts with `_` (since Pydantic
330
+ # uses this to indicate private fields). We can work around this by using an alias.
331
+ alias = None
332
+ if name.startswith("_"):
333
+ field_name, alias = (name.lstrip("_"), name)
334
+ else:
335
+ field_name, alias = (name, None)
336
+
329
337
  if param.default != inspect.Parameter.empty:
330
- field = Field(default=param.default)
338
+ field = Field(default=param.default, alias=alias)
331
339
  else:
332
- field = Field()
340
+ field = Field(alias=alias)
333
341
 
334
342
  # Add the field to our fields dict
335
- fields[name] = (annotation, field)
343
+ fields[field_name] = (annotation, field)
336
344
 
337
345
  return create_model(func.__name__, **fields)
338
346
 
chatlas/_version.py CHANGED
@@ -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.11.1'
32
- __version_tuple__ = version_tuple = (0, 11, 1)
31
+ __version__ = version = '0.13.0'
32
+ __version_tuple__ = version_tuple = (0, 13, 0)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -3,7 +3,7 @@
3
3
  # ---------------------------------------------------------
4
4
 
5
5
 
6
- from typing import Iterable, Literal, Mapping, Optional, TypedDict, Union
6
+ from typing import Iterable, Literal, Mapping, Optional, Sequence, TypedDict, Union
7
7
 
8
8
  import anthropic
9
9
  import anthropic.types.message_param
@@ -48,7 +48,7 @@ class SubmitInputArgs(TypedDict, total=False):
48
48
  str,
49
49
  ]
50
50
  service_tier: Union[Literal["auto", "standard_only"], anthropic.NotGiven]
51
- stop_sequences: Union[list[str], anthropic.NotGiven]
51
+ stop_sequences: Union[Sequence[str], anthropic.NotGiven]
52
52
  stream: Union[Literal[False], Literal[True], anthropic.NotGiven]
53
53
  system: Union[
54
54
  str,
@@ -3,14 +3,14 @@
3
3
  # ---------------------------------------------------------
4
4
 
5
5
 
6
- from typing import Mapping, Optional, TypedDict, Union
6
+ from typing import Awaitable, Callable, Mapping, Optional, TypedDict, Union
7
7
 
8
8
  import httpx
9
9
  import openai
10
10
 
11
11
 
12
12
  class ChatClientArgs(TypedDict, total=False):
13
- api_key: str | None
13
+ api_key: Union[str, Callable[[], Awaitable[str]], None]
14
14
  organization: str | None
15
15
  project: str | None
16
16
  webhook_secret: str | None
@@ -2,7 +2,7 @@
2
2
  # Do not modify this file. It was generated by `scripts/generate_typed_dicts.py`.
3
3
  # ---------------------------------------------------------
4
4
 
5
- from typing import Mapping, Optional, TypedDict
5
+ from typing import Awaitable, Callable, Mapping, Optional, TypedDict, Union
6
6
 
7
7
  import httpx
8
8
  import openai
@@ -12,7 +12,7 @@ class ChatAzureClientArgs(TypedDict, total=False):
12
12
  azure_endpoint: str | None
13
13
  azure_deployment: str | None
14
14
  api_version: str | None
15
- api_key: str | None
15
+ api_key: Union[str, Callable[[], Awaitable[str]], None]
16
16
  azure_ad_token: str | None
17
17
  organization: str | None
18
18
  project: str | None
@@ -3,7 +3,7 @@
3
3
  # ---------------------------------------------------------
4
4
 
5
5
 
6
- from typing import Iterable, Literal, Mapping, Optional, TypedDict, Union
6
+ from typing import Iterable, Literal, Mapping, Optional, Sequence, TypedDict, Union
7
7
 
8
8
  import openai
9
9
  import openai.types.chat.chat_completion_allowed_tool_choice_param
@@ -148,7 +148,7 @@ class SubmitInputArgs(TypedDict, total=False):
148
148
  service_tier: Union[
149
149
  Literal["auto", "default", "flex", "scale", "priority"], None, openai.NotGiven
150
150
  ]
151
- stop: Union[str, None, list[str], openai.NotGiven]
151
+ stop: Union[str, None, Sequence[str], openai.NotGiven]
152
152
  store: Union[bool, None, openai.NotGiven]
153
153
  stream: Union[Literal[False], None, Literal[True], openai.NotGiven]
154
154
  stream_options: Union[
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatlas
3
- Version: 0.11.1
3
+ Version: 0.13.0
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
@@ -44,6 +44,7 @@ Requires-Dist: pillow; extra == 'dev'
44
44
  Requires-Dist: python-dotenv; extra == 'dev'
45
45
  Requires-Dist: ruff>=0.6.5; extra == 'dev'
46
46
  Requires-Dist: shiny; extra == 'dev'
47
+ Requires-Dist: shinychat; extra == 'dev'
47
48
  Requires-Dist: snowflake-ml-python>=1.8.4; extra == 'dev'
48
49
  Requires-Dist: tenacity; extra == 'dev'
49
50
  Requires-Dist: tiktoken; extra == 'dev'
@@ -1,8 +1,10 @@
1
- chatlas/__init__.py,sha256=CyViGMiz50clcVu3vpZgOq_qP4hmoYGOlcHKlRPcLJo,2416
2
- chatlas/_auto.py,sha256=-s7XGzsKLX4RipWtk4WOE8iKbOBhXPUPtI0-63PpXCY,5660
1
+ chatlas/__init__.py,sha256=M3zK10LguXW6bybDqatRV9SXd0y6axYu5QzENjbOVd0,2633
2
+ chatlas/_auto.py,sha256=aeMN2_EM-xK-Yx5JaCuwYRZZ29eqn_0oM7QR5zayrec,8912
3
+ chatlas/_batch_chat.py,sha256=1KkHENB-l7VmhCizhdvbJO5WQmRntQS6EvcSJ6VLgvM,5546
4
+ chatlas/_batch_job.py,sha256=2__JIOo_JpcQyAAzO07r6eS4urpAxEc9m7_zsjFieQw,7359
3
5
  chatlas/_callbacks.py,sha256=3RpPaOQonTqScjXbaShgKJ1Rc-YxzWerxKRBjVssFnc,1838
4
- chatlas/_chat.py,sha256=2AKkKHOJz6eV2Xulxle3_sl4PteP-_mj2YXaEbCd0gU,85375
5
- chatlas/_content.py,sha256=RJuJaTkyOjlXPmQYUww_a-lLV1LKU2C25tuTOCvb9vA,22756
6
+ chatlas/_chat.py,sha256=oESXNVzDCJ1DpV_fBRgwK6N_fs_EoJGrlez5dJjqx5c,90664
7
+ chatlas/_content.py,sha256=BdJQ5G5onT9Cf1tNFeXsCWWTD2zSIjWz50FYIk6_DDI,22767
6
8
  chatlas/_content_image.py,sha256=EUK6wAint-JatLsiwvaPDu4D3W-NcIsDCkzABkXgfDg,8304
7
9
  chatlas/_content_pdf.py,sha256=cffeuJxzhUDukQ-Srkmpy62M8X12skYpU_FVq-Wvya4,2420
8
10
  chatlas/_display.py,sha256=wyQzSc6z1VqrJfkTLkw1wQcti9s1Pr4qT8UxFJESn4U,4664
@@ -11,8 +13,8 @@ chatlas/_live_render.py,sha256=UMZltE35LxziDKPMEeDwQ9meZ95SeqwhJi7j-y9pcro,4004
11
13
  chatlas/_logging.py,sha256=weKvXZDIZ88X7X61ruXM_S0AAhQ5mgiW9dR-km8x7Mg,3324
12
14
  chatlas/_mcp_manager.py,sha256=smMXeKZzP90MrlCdnTHMyo7AWHwl7J2jkU8dKSlnEsQ,10237
13
15
  chatlas/_merge.py,sha256=SGj_BetgA7gaOqSBKOhYmW3CYeQKTEehFrXvx3y4OYE,3924
14
- chatlas/_provider.py,sha256=k0rJ2uzGDacXVJZZVoLlySNCSFOjYbOC7k_VUT7j_Ms,6453
15
- chatlas/_provider_anthropic.py,sha256=cOBkAEj6gyl0NGdLk3QvI6pruZZ2fUsXJZAN2i3_j3k,27394
16
+ chatlas/_provider.py,sha256=-5Oyq8tehHJtbBWQUyFUvdqTqZNUcOq2pO5qfAw5oQo,9057
17
+ chatlas/_provider_anthropic.py,sha256=sPPEaDObGuY7JDqU533wlUDA-HaX3sumYWaD3kdG4nE,30964
16
18
  chatlas/_provider_cloudflare.py,sha256=vFbqgQPmosopJa9qsVxTkjPn4vYC_wOlgqa6_QmwTho,5227
17
19
  chatlas/_provider_databricks.py,sha256=JIOTm0HMe0qVAt8eS0WgGKugBwBdmL80JHLFH59ongU,4850
18
20
  chatlas/_provider_deepseek.py,sha256=6nPtPSo-Po6sD4i8PZJHuI5T2oATpLi5djXFGdlserk,4906
@@ -22,33 +24,33 @@ chatlas/_provider_groq.py,sha256=XB2JDyuF95CcSbNkgk7JHcuy9KCW7hxTVaONDSjK8U8,367
22
24
  chatlas/_provider_huggingface.py,sha256=feJ416X0UdtyoeHZbkgolFf62D7zxNwM7i_X3NYsQQw,4669
23
25
  chatlas/_provider_mistral.py,sha256=-p4rut0KCn-PrwnOlvr6lK8-K-OXvc5H9vTX-rCzUkk,5309
24
26
  chatlas/_provider_ollama.py,sha256=jFAu4v0NLUwdG_W_nKagBHOah0VKl_auTsgcYinP9rI,4119
25
- chatlas/_provider_openai.py,sha256=SwzEBwA491HOL6YvEI5soDQIVXnSLpwMmU-0DC8k7QA,26422
27
+ chatlas/_provider_openai.py,sha256=f8ijdXMMHy17VcuA2ImMpaYvaiKjHzcnTdR8LH1xE40,30654
26
28
  chatlas/_provider_openrouter.py,sha256=9sCXvROVIiUdwfEbkVA-15_kc6ouFUP2uV2MmUe2rFk,4385
27
29
  chatlas/_provider_perplexity.py,sha256=5q_LsUCJQ5w-jRveLDMPvZTX-GU2TVURp65mUMyDh10,4248
28
30
  chatlas/_provider_portkey.py,sha256=6wKrLZmKVxOqyO6P3HBgWqPe7y1N8une_1wp0aJq7pU,4087
29
31
  chatlas/_provider_snowflake.py,sha256=G66tG_zs_cIlrHaHY96GvBreTNHHk1O5012Y0BtYRqI,24578
30
32
  chatlas/_tokens.py,sha256=QUsBLNJPgXk8vovcG5JdQU8NarCv7FRpOVBdgFkBgHs,5388
31
33
  chatlas/_tokens_old.py,sha256=L9d9oafrXvEx2u4nIn_Jjn7adnQyLBnYBuPwJUE8Pl8,5005
32
- chatlas/_tools.py,sha256=Nm-6FPUc5s58ocToc1vNgBzCqenUOgnvHW52K9Ychoc,12029
34
+ chatlas/_tools.py,sha256=8rhGOsEviBJXk5Qb-a1RRb_C-DE2T3DOeN6IhblkxqI,12408
33
35
  chatlas/_turn.py,sha256=yK7alUxeP8d2iBc7amyz20BtEqcpvX6BCwWZsnlQ5R4,4515
34
36
  chatlas/_typing_extensions.py,sha256=BXmbhywjm5ssmyVLGwyP_5TWZMAobzrrgZLYkB6_etE,1076
35
37
  chatlas/_utils.py,sha256=Kku2fa1mvTYCr5D28VxE6-fwfy2e2doCi-eKQkLEg4Y,4686
36
- chatlas/_version.py,sha256=2oVGHl_4oC5-CorjiyQuPRViZiNckFg-qYTqNnQsPWE,706
38
+ chatlas/_version.py,sha256=2OwVfU6dYXS16K64p8xtyqfPM0bDW3r_7GeF3HzefhA,706
37
39
  chatlas/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
40
  chatlas/data/prices.json,sha256=X6qALp-dWc4nfus9lIqHoKzk3PZDPHTLoxxcN2m6fXc,62645
39
41
  chatlas/types/__init__.py,sha256=1n0xrJ7TRIKsZ2z06FLFgGqfKMFtXSIxxPvJ2j0hvPw,850
40
42
  chatlas/types/anthropic/__init__.py,sha256=OwubA-DPHYpYo0XyRyAFwftOI0mOxtHzAyhUSLcDx54,417
41
43
  chatlas/types/anthropic/_client.py,sha256=t_tnOzzsW1xWNADkNoAuZJYoE9QJ8ie7DQNnFO1pvoM,697
42
44
  chatlas/types/anthropic/_client_bedrock.py,sha256=2J6U1QcSx1KwiiHfXs3i4YEXDXw11sp-x3iLOuESrgQ,792
43
- chatlas/types/anthropic/_submit.py,sha256=o5bpKEne6lqBz4YBLoYwRLKCVmBIdzRetiMCoOdUfb0,3661
45
+ chatlas/types/anthropic/_submit.py,sha256=X7ER31k7bTZGJ9X9u8Mx-X4enJC13W0VQzt8Wz-mLeQ,3675
44
46
  chatlas/types/google/__init__.py,sha256=ZJhi8Kwvio2zp8T1TQqmvdHqkS-Khb6BGESPjREADgo,337
45
47
  chatlas/types/google/_client.py,sha256=t7aKbxYq_xOA1Z3RnWcjewifdQFSHi7vKEj6MyKMCJk,729
46
48
  chatlas/types/google/_submit.py,sha256=19Ji4fAo1lTCbNSpR6Yi0i64RJwMGBdiZKQcnoDNRwY,1796
47
49
  chatlas/types/openai/__init__.py,sha256=Q2RAr1bSH1nHsxICK05nAmKmxdhKmhbBkWD_XHiVSrI,411
48
- chatlas/types/openai/_client.py,sha256=SttisELwAd52_Je_5q3RfWGdX5wbg2CoGbxhS8ThS0A,792
49
- chatlas/types/openai/_client_azure.py,sha256=b8Hr7iKYA5-sq9r7uEqbBFv9yo3itppmHIgkEGvChMs,896
50
- chatlas/types/openai/_submit.py,sha256=rhft1h7zy6eSlSBLkt7ZAySFh-8WnR5UEG-BXaFTxag,7815
51
- chatlas-0.11.1.dist-info/METADATA,sha256=auxRj9RSC9kjVBSANlwfWWoOTQQ1MfroR59IoCWVI_M,5594
52
- chatlas-0.11.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
53
- chatlas-0.11.1.dist-info/licenses/LICENSE,sha256=zyuGzPOC7CcbOaBHsQ3UEyKYRO56KDUkor0OA4LqqDg,1081
54
- chatlas-0.11.1.dist-info/RECORD,,
50
+ chatlas/types/openai/_client.py,sha256=mAoQftcJIp0ssIhS8q3TIW9u6zTRNtYDmpZJO8L0mC0,849
51
+ chatlas/types/openai/_client_azure.py,sha256=Tf_PFRl0QAj4Nk5CD0ZNIO-SRsT39bVkEJlUTry1fb8,960
52
+ chatlas/types/openai/_submit.py,sha256=EDtIUFcNIJ5QAt0wVyBXvUshK8FA9e86wcZDQ_HUOYs,7829
53
+ chatlas-0.13.0.dist-info/METADATA,sha256=bpXFGxJZFx1hIkGuIWfC-MoHLhT3cDzsotmuhtFyktY,5635
54
+ chatlas-0.13.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
55
+ chatlas-0.13.0.dist-info/licenses/LICENSE,sha256=zyuGzPOC7CcbOaBHsQ3UEyKYRO56KDUkor0OA4LqqDg,1081
56
+ chatlas-0.13.0.dist-info/RECORD,,