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/__init__.py CHANGED
@@ -1,5 +1,11 @@
1
1
  from . import types
2
2
  from ._auto import ChatAuto
3
+ from ._batch_chat import (
4
+ batch_chat,
5
+ batch_chat_completed,
6
+ batch_chat_structured,
7
+ batch_chat_text,
8
+ )
3
9
  from ._chat import Chat
4
10
  from ._content import (
5
11
  ContentToolRequest,
@@ -36,6 +42,10 @@ except ImportError: # pragma: no cover
36
42
  __version__ = "0.0.0" # stub value for docs
37
43
 
38
44
  __all__ = (
45
+ "batch_chat",
46
+ "batch_chat_completed",
47
+ "batch_chat_structured",
48
+ "batch_chat_text",
39
49
  "ChatAnthropic",
40
50
  "ChatAuto",
41
51
  "ChatBedrockAnthropic",
chatlas/_auto.py CHANGED
@@ -1,32 +1,46 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ import warnings
4
5
  from typing import Callable, Literal, Optional
5
6
 
6
7
  import orjson
7
8
 
8
9
  from ._chat import Chat
9
10
  from ._provider_anthropic import ChatAnthropic, ChatBedrockAnthropic
11
+ from ._provider_cloudflare import ChatCloudflare
10
12
  from ._provider_databricks import ChatDatabricks
13
+ from ._provider_deepseek import ChatDeepSeek
11
14
  from ._provider_github import ChatGithub
12
15
  from ._provider_google import ChatGoogle, ChatVertex
13
16
  from ._provider_groq import ChatGroq
17
+ from ._provider_huggingface import ChatHuggingFace
18
+ from ._provider_mistral import ChatMistral
14
19
  from ._provider_ollama import ChatOllama
15
20
  from ._provider_openai import ChatAzureOpenAI, ChatOpenAI
21
+ from ._provider_openrouter import ChatOpenRouter
16
22
  from ._provider_perplexity import ChatPerplexity
23
+ from ._provider_portkey import ChatPortkey
17
24
  from ._provider_snowflake import ChatSnowflake
25
+ from ._utils import MISSING_TYPE as DEPRECATED_TYPE
18
26
 
19
27
  AutoProviders = Literal[
20
28
  "anthropic",
21
29
  "bedrock-anthropic",
30
+ "cloudflare",
22
31
  "databricks",
32
+ "deep-seek",
23
33
  "github",
24
34
  "google",
25
35
  "groq",
36
+ "hugging-face",
37
+ "mistral",
26
38
  "ollama",
27
39
  "openai",
28
40
  "azure-openai",
41
+ "open-router",
29
42
  "perplexity",
43
+ "portkey",
30
44
  "snowflake",
31
45
  "vertex",
32
46
  ]
@@ -34,41 +48,40 @@ AutoProviders = Literal[
34
48
  _provider_chat_model_map: dict[AutoProviders, Callable[..., Chat]] = {
35
49
  "anthropic": ChatAnthropic,
36
50
  "bedrock-anthropic": ChatBedrockAnthropic,
51
+ "cloudflare": ChatCloudflare,
37
52
  "databricks": ChatDatabricks,
53
+ "deep-seek": ChatDeepSeek,
38
54
  "github": ChatGithub,
39
55
  "google": ChatGoogle,
40
56
  "groq": ChatGroq,
57
+ "hugging-face": ChatHuggingFace,
58
+ "mistral": ChatMistral,
41
59
  "ollama": ChatOllama,
42
60
  "openai": ChatOpenAI,
43
61
  "azure-openai": ChatAzureOpenAI,
62
+ "open-router": ChatOpenRouter,
44
63
  "perplexity": ChatPerplexity,
64
+ "portkey": ChatPortkey,
45
65
  "snowflake": ChatSnowflake,
46
66
  "vertex": ChatVertex,
47
67
  }
48
68
 
69
+ DEPRECATED = DEPRECATED_TYPE()
70
+
49
71
 
50
72
  def ChatAuto(
51
- system_prompt: Optional[str] = None,
73
+ provider_model: Optional[str] = None,
52
74
  *,
53
- provider: Optional[AutoProviders] = None,
54
- model: Optional[str] = None,
75
+ system_prompt: Optional[str] = None,
76
+ provider: AutoProviders | DEPRECATED_TYPE = DEPRECATED,
77
+ model: str | DEPRECATED_TYPE = DEPRECATED,
55
78
  **kwargs,
56
79
  ) -> Chat:
57
80
  """
58
- Use environment variables (env vars) to configure the Chat provider and model.
81
+ Chat with any provider.
59
82
 
60
- Creates a :class:`~chatlas.Chat` instance based on the specified provider.
61
- The provider may be specified through the `provider` parameter and/or the
62
- `CHATLAS_CHAT_PROVIDER` env var. If both are set, the env var takes
63
- precedence. Similarly, the provider's model may be specified through the
64
- `model` parameter and/or the `CHATLAS_CHAT_MODEL` env var. Also, additional
65
- configuration may be provided through the `kwargs` parameter and/or the
66
- `CHATLAS_CHAT_ARGS` env var (as a JSON string). In this case, when both are
67
- set, they are merged, with the env var arguments taking precedence.
68
-
69
- As a result, `ChatAuto()` provides a convenient way to set a default
70
- provider and model in your Python code, while allowing you to override
71
- these settings through env vars (i.e., without modifying your code).
83
+ This is a generic interface to all the other `Chat*()` functions, allowing
84
+ you to pick the provider (and model) with a simple string.
72
85
 
73
86
  Prerequisites
74
87
  -------------
@@ -86,55 +99,101 @@ def ChatAuto(
86
99
  Python packages.
87
100
  :::
88
101
 
89
-
90
102
  Examples
91
103
  --------
92
- First, set the environment variables for the provider, arguments, and API key:
93
104
 
94
- ```bash
95
- export CHATLAS_CHAT_PROVIDER=anthropic
96
- export CHATLAS_CHAT_MODEL=claude-3-haiku-20240229
97
- export CHATLAS_CHAT_ARGS='{"kwargs": {"max_retries": 3}}'
98
- export ANTHROPIC_API_KEY=your_api_key
105
+ `ChatAuto()` makes it easy to switch between different chat providers and models.
106
+
107
+ ```python
108
+ import pandas as pd
109
+ from chatlas import ChatAuto
110
+
111
+ # Default provider (OpenAI) & model
112
+ chat = ChatAuto()
113
+ print(chat.provider.name)
114
+ print(chat.provider.model)
115
+
116
+ # Different provider (Anthropic) & default model
117
+ chat = ChatAuto("anthropic")
118
+
119
+ # List models available through the provider
120
+ models = chat.list_models()
121
+ print(pd.DataFrame(models))
122
+
123
+ # Choose specific provider/model (Claude Sonnet 4)
124
+ chat = ChatAuto("anthropic/claude-sonnet-4-0")
99
125
  ```
100
126
 
101
- Then, you can use the `ChatAuto` function to create a Chat instance:
127
+ The default provider/model can also be controlled through an environment variable:
128
+
129
+ ```bash
130
+ export CHATLAS_CHAT_PROVIDER_MODEL="anthropic/claude-sonnet-4-0"
131
+ ```
102
132
 
103
133
  ```python
104
134
  from chatlas import ChatAuto
105
135
 
106
136
  chat = ChatAuto()
107
- chat.chat("What is the capital of France?")
137
+ print(chat.provider.name) # anthropic
138
+ print(chat.provider.model) # claude-sonnet-4-0
139
+ ```
140
+
141
+ For application-specific configurations, consider defining your own environment variables:
142
+
143
+ ```bash
144
+ export MYAPP_PROVIDER_MODEL="google/gemini-2.5-flash"
145
+ ```
146
+
147
+ And passing them to `ChatAuto()` as an alternative way to configure the provider/model:
148
+
149
+ ```python
150
+ import os
151
+ from chatlas import ChatAuto
152
+
153
+ chat = ChatAuto(os.getenv("MYAPP_PROVIDER_MODEL"))
154
+ print(chat.provider.name) # google
155
+ print(chat.provider.model) # gemini-2.5-flash
108
156
  ```
109
157
 
110
158
  Parameters
111
159
  ----------
160
+ provider_model
161
+ The name of the provider and model to use in the format
162
+ `"{provider}/{model}"`. Providers are strings formatted in kebab-case,
163
+ e.g. to use `ChatBedrockAnthropic` set `provider="bedrock-anthropic"`,
164
+ and models are the provider-specific model names, e.g.
165
+ `"claude-3-7-sonnet-20250219"`. The `/{model}` portion may also be
166
+ omitted, in which case, the default model for that provider will be
167
+ used.
168
+
169
+ If no value is provided, the `CHATLAS_CHAT_PROVIDER_MODEL` environment
170
+ variable will be consulted for a fallback value. If this variable is also
171
+ not set, a default value of `"openai"` is used.
112
172
  system_prompt
113
173
  A system prompt to set the behavior of the assistant.
114
174
  provider
115
- The name of the default chat provider to use. Providers are strings
116
- formatted in kebab-case, e.g. to use `ChatBedrockAnthropic` set
117
- `provider="bedrock-anthropic"`.
118
-
119
- This value can also be provided via the `CHATLAS_CHAT_PROVIDER`
120
- environment variable, which takes precedence over `provider`
121
- when set.
175
+ Deprecated; use `provider_model` instead.
122
176
  model
123
- The name of the default model to use. This value can also be provided
124
- via the `CHATLAS_CHAT_MODEL` environment variable, which takes
125
- precedence over `model` when set.
177
+ Deprecated; use `provider_model` instead.
126
178
  **kwargs
127
- Additional keyword arguments to pass to the Chat constructor. See the
179
+ Additional keyword arguments to pass to the `Chat` constructor. See the
128
180
  documentation for each provider for more details on the available
129
181
  options.
130
182
 
131
183
  These arguments can also be provided via the `CHATLAS_CHAT_ARGS`
132
- environment variable as a JSON string. When provided, the options
133
- in the `CHATLAS_CHAT_ARGS` envvar take precedence over the options
134
- passed to `kwargs`.
184
+ environment variable as a JSON string. When any additional arguments are
185
+ provided to `ChatAuto()`, the env var is ignored.
186
+
187
+ Note that `system_prompt` and `turns` can't be set via environment variables.
188
+ They must be provided/set directly to/on `ChatAuto()`.
135
189
 
136
- Note that `system_prompt` and `turns` in `kwargs` or in
137
- `CHATLAS_CHAT_ARGS` are ignored.
190
+ Note
191
+ ----
192
+ If you want to work with a specific provider, but don't know what models are
193
+ available (or the exact model name), use
194
+ `ChatAuto('provider_name').list_models()` to list available models. Another
195
+ option is to use the provider more directly (e.g., `ChatAnthropic()`). There,
196
+ the `model` parameter may have type hints for available models.
138
197
 
139
198
  Returns
140
199
  -------
@@ -147,32 +206,85 @@ def ChatAuto(
147
206
  If no valid provider is specified either through parameters or
148
207
  environment variables.
149
208
  """
150
- the_provider = os.environ.get("CHATLAS_CHAT_PROVIDER", provider)
209
+ if provider is not DEPRECATED:
210
+ warn_deprecated_param("provider")
211
+
212
+ if model is not DEPRECATED:
213
+ if provider is DEPRECATED:
214
+ raise ValueError(
215
+ "The `model` parameter is deprecated and cannot be used without the `provider` parameter. "
216
+ "Use `provider_model` instead."
217
+ )
218
+ warn_deprecated_param("model")
219
+
220
+ if provider_model is None:
221
+ provider_model = os.environ.get("CHATLAS_CHAT_PROVIDER_MODEL")
222
+
223
+ # Backwards compatibility: construct from old env vars as a fallback
224
+ if provider_model is None:
225
+ env_provider = get_legacy_env_var("CHATLAS_CHAT_PROVIDER", provider)
226
+ env_model = get_legacy_env_var("CHATLAS_CHAT_MODEL", model)
227
+
228
+ if env_provider:
229
+ provider_model = env_provider
230
+ if env_model:
231
+ provider_model += f"/{env_model}"
232
+
233
+ # Fall back to OpenAI if nothing is specified
234
+ if provider_model is None:
235
+ provider_model = "openai"
236
+
237
+ if "/" in provider_model:
238
+ the_provider, the_model = provider_model.split("/", 1)
239
+ else:
240
+ the_provider, the_model = provider_model, None
151
241
 
152
- if the_provider is None:
153
- raise ValueError(
154
- "Provider name is required as parameter or `CHATLAS_CHAT_PROVIDER` must be set."
155
- )
156
242
  if the_provider not in _provider_chat_model_map:
157
243
  raise ValueError(
158
244
  f"Provider name '{the_provider}' is not a known chatlas provider: "
159
245
  f"{', '.join(_provider_chat_model_map.keys())}"
160
246
  )
161
247
 
162
- # `system_prompt` and `turns` always come from `ChatAuto()`
163
- base_args = {"system_prompt": system_prompt}
164
-
165
- if env_model := os.environ.get("CHATLAS_CHAT_MODEL"):
166
- model = env_model
167
-
168
- if model:
169
- base_args["model"] = model
248
+ # `system_prompt`, `turns` and `model` always come from `ChatAuto()`
249
+ base_args = {
250
+ "system_prompt": system_prompt,
251
+ "turns": None,
252
+ "model": the_model,
253
+ }
170
254
 
255
+ # Environment kwargs, used only if no kwargs provided
171
256
  env_kwargs = {}
172
- if env_kwargs_str := os.environ.get("CHATLAS_CHAT_ARGS"):
173
- env_kwargs = orjson.loads(env_kwargs_str)
174
-
175
- kwargs = {**kwargs, **env_kwargs, **base_args}
176
- kwargs = {k: v for k, v in kwargs.items() if v is not None}
177
-
178
- return _provider_chat_model_map[the_provider](**kwargs)
257
+ if not kwargs:
258
+ env_kwargs = orjson.loads(os.environ.get("CHATLAS_CHAT_ARGS", "{}"))
259
+
260
+ final_kwargs = {**env_kwargs, **kwargs, **base_args}
261
+ final_kwargs = {k: v for k, v in final_kwargs.items() if v is not None}
262
+
263
+ return _provider_chat_model_map[the_provider](**final_kwargs)
264
+
265
+
266
+ def get_legacy_env_var(
267
+ env_var_name: str,
268
+ default: str | DEPRECATED_TYPE,
269
+ ) -> str | None:
270
+ env_value = os.environ.get(env_var_name)
271
+ if env_value:
272
+ warnings.warn(
273
+ f"The '{env_var_name}' environment variable is deprecated. "
274
+ "Use 'CHATLAS_CHAT_PROVIDER_MODEL' instead.",
275
+ DeprecationWarning,
276
+ stacklevel=3,
277
+ )
278
+ return env_value
279
+ elif isinstance(default, DEPRECATED_TYPE):
280
+ return None
281
+ else:
282
+ return default
283
+
284
+
285
+ def warn_deprecated_param(param_name: str, stacklevel: int = 3) -> None:
286
+ warnings.warn(
287
+ f"The '{param_name}' parameter is deprecated. Use 'provider_model' instead.",
288
+ DeprecationWarning,
289
+ stacklevel=stacklevel,
290
+ )
chatlas/_batch_chat.py ADDED
@@ -0,0 +1,211 @@
1
+ """
2
+ Batch chat processing for submitting multiple requests simultaneously.
3
+
4
+ This module provides functionality for submitting multiple chat requests
5
+ in batches to providers that support it (currently OpenAI and Anthropic).
6
+ Batch processing can take up to 24 hours but offers significant cost savings
7
+ (up to 50% less than regular requests).
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import copy
13
+ from pathlib import Path
14
+ from typing import TypeVar, Union
15
+
16
+ from pydantic import BaseModel
17
+
18
+ from ._batch_job import BatchJob, ContentT
19
+ from ._chat import Chat
20
+
21
+ ChatT = TypeVar("ChatT", bound=Chat)
22
+ BaseModelT = TypeVar("BaseModelT", bound=BaseModel)
23
+
24
+
25
+ def batch_chat(
26
+ chat: ChatT,
27
+ prompts: list[ContentT] | list[list[ContentT]],
28
+ path: Union[str, Path],
29
+ wait: bool = True,
30
+ ) -> list[ChatT | None]:
31
+ """
32
+ Submit multiple chat requests in a batch.
33
+
34
+ This function allows you to submit multiple chat requests simultaneously
35
+ using provider batch APIs (currently OpenAI and Anthropic). Batch processing
36
+ can take up to 24 hours but offers significant cost savings.
37
+
38
+ Parameters
39
+ ----------
40
+ chat
41
+ Chat instance to use for the batch
42
+ prompts
43
+ List of prompts to process. Each can be a string or list of strings.
44
+ path
45
+ Path to file (with .json extension) to store batch state
46
+ wait
47
+ If True, wait for batch to complete. If False, return None if incomplete.
48
+
49
+ Returns
50
+ -------
51
+ List of Chat objects (one per prompt) if complete, None if wait=False and incomplete.
52
+ Individual Chat objects may be None if their request failed.
53
+
54
+ Example
55
+ -------
56
+
57
+ ```python
58
+ from chatlas import ChatOpenAI
59
+
60
+ chat = ChatOpenAI()
61
+ prompts = [
62
+ "What's the capital of France?",
63
+ "What's the capital of Germany?",
64
+ "What's the capital of Italy?",
65
+ ]
66
+
67
+ chats = batch_chat(chat, prompts, "capitals.json")
68
+ for i, result_chat in enumerate(chats):
69
+ if result_chat:
70
+ print(f"Prompt {i + 1}: {result_chat.get_last_turn().text}")
71
+ ```
72
+ """
73
+ job = BatchJob(chat, prompts, path, wait=wait)
74
+ job.step_until_done()
75
+
76
+ chats = []
77
+ assistant_turns = job.result_turns()
78
+ for user, assistant in zip(job.user_turns, assistant_turns):
79
+ if assistant is not None:
80
+ new_chat = copy.deepcopy(chat)
81
+ new_chat.add_turn(user)
82
+ new_chat.add_turn(assistant)
83
+ chats.append(new_chat)
84
+ else:
85
+ chats.append(None)
86
+
87
+ return chats
88
+
89
+
90
+ def batch_chat_text(
91
+ chat: Chat,
92
+ prompts: list[ContentT] | list[list[ContentT]],
93
+ path: Union[str, Path],
94
+ wait: bool = True,
95
+ ) -> list[str | None]:
96
+ """
97
+ Submit multiple chat requests in a batch and return text responses.
98
+
99
+ This is a convenience function that returns just the text of the responses
100
+ rather than full Chat objects.
101
+
102
+ Parameters
103
+ ----------
104
+ chat
105
+ Chat instance to use for the batch
106
+ prompts
107
+ List of prompts to process
108
+ path
109
+ Path to file (with .json extension) to store batch state
110
+ wait
111
+ If True, wait for batch to complete
112
+
113
+ Return
114
+ ------
115
+ List of text responses (or None for failed requests)
116
+ """
117
+ chats = batch_chat(chat, prompts, path, wait=wait)
118
+
119
+ texts = []
120
+ for x in chats:
121
+ if x is None:
122
+ texts.append(None)
123
+ continue
124
+ last_turn = x.get_last_turn()
125
+ if last_turn is None:
126
+ texts.append(None)
127
+ continue
128
+ texts.append(last_turn.text)
129
+
130
+ return texts
131
+
132
+
133
+ def batch_chat_structured(
134
+ chat: Chat,
135
+ prompts: list[ContentT] | list[list[ContentT]],
136
+ path: Union[str, Path],
137
+ data_model: type[BaseModelT],
138
+ wait: bool = True,
139
+ ) -> list[BaseModelT | None]:
140
+ """
141
+ Submit multiple structured data requests in a batch.
142
+
143
+ Parameters
144
+ ----------
145
+ chat
146
+ Chat instance to use for the batch
147
+ prompts
148
+ List of prompts to process
149
+ path
150
+ Path to file (with .json extension) to store batch state
151
+ data_model
152
+ Pydantic model class for structured responses
153
+ wait
154
+ If True, wait for batch to complete
155
+
156
+ Return
157
+ ------
158
+ List of structured data objects (or None for failed requests)
159
+ """
160
+ job = BatchJob(chat, prompts, path, data_model=data_model, wait=wait)
161
+ result = job.step_until_done()
162
+
163
+ if result is None:
164
+ return []
165
+
166
+ res: list[BaseModelT | None] = []
167
+ assistant_turns = job.result_turns()
168
+ for turn in assistant_turns:
169
+ if turn is None:
170
+ res.append(None)
171
+ else:
172
+ json = chat._extract_turn_json(turn)
173
+ model = data_model.model_validate(json)
174
+ res.append(model)
175
+
176
+ return res
177
+
178
+
179
+ def batch_chat_completed(
180
+ chat: Chat,
181
+ prompts: list[ContentT] | list[list[ContentT]],
182
+ path: Union[str, Path],
183
+ ) -> bool:
184
+ """
185
+ Check if a batch job is completed without waiting.
186
+
187
+ Parameters
188
+ ----------
189
+ chat
190
+ Chat instance used for the batch
191
+ prompts
192
+ List of prompts used for the batch
193
+ path
194
+ Path to batch state file
195
+
196
+ Returns
197
+ -------
198
+ True if batch is complete, False otherwise
199
+ """
200
+ job = BatchJob(chat, prompts, path, wait=False)
201
+ stage = job.stage
202
+
203
+ if stage == "submitting":
204
+ return False
205
+ elif stage == "waiting":
206
+ status = job._poll()
207
+ return not status.working
208
+ elif stage == "retrieving" or stage == "done":
209
+ return True
210
+ else:
211
+ raise ValueError(f"Unknown batch stage: {stage}")