chatlas 0.4.0__py3-none-any.whl → 0.5.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,6 @@
1
1
  from . import types
2
2
  from ._anthropic import ChatAnthropic, ChatBedrockAnthropic
3
+ from ._auto import ChatAuto
3
4
  from ._chat import Chat
4
5
  from ._content_image import content_image_file, content_image_plot, content_image_url
5
6
  from ._github import ChatGithub
@@ -10,12 +11,19 @@ from ._ollama import ChatOllama
10
11
  from ._openai import ChatAzureOpenAI, ChatOpenAI
11
12
  from ._perplexity import ChatPerplexity
12
13
  from ._provider import Provider
14
+ from ._snowflake import ChatSnowflake
13
15
  from ._tokens import token_usage
14
16
  from ._tools import Tool
15
17
  from ._turn import Turn
16
18
 
19
+ try:
20
+ from ._version import version as __version__
21
+ except ImportError: # pragma: no cover
22
+ __version__ = "0.0.0" # stub value for docs
23
+
17
24
  __all__ = (
18
25
  "ChatAnthropic",
26
+ "ChatAuto",
19
27
  "ChatBedrockAnthropic",
20
28
  "ChatGithub",
21
29
  "ChatGoogle",
@@ -24,6 +32,7 @@ __all__ = (
24
32
  "ChatOpenAI",
25
33
  "ChatAzureOpenAI",
26
34
  "ChatPerplexity",
35
+ "ChatSnowflake",
27
36
  "ChatVertex",
28
37
  "Chat",
29
38
  "content_image_file",
chatlas/_anthropic.py CHANGED
@@ -72,7 +72,7 @@ def ChatAnthropic(
72
72
  ::: {.callout-note}
73
73
  ## API key
74
74
 
75
- Note that a Claude Prop membership does not give you the ability to call
75
+ Note that a Claude Pro membership does not give you the ability to call
76
76
  models via the API. You will need to go to the [developer
77
77
  console](https://console.anthropic.com/account/keys) to sign up (and pay
78
78
  for) a developer account that will give you an API key that you can use with
@@ -82,7 +82,7 @@ def ChatAnthropic(
82
82
  ::: {.callout-note}
83
83
  ## Python requirements
84
84
 
85
- `ChatAnthropic` requires the `anthropic` package (e.g., `pip install anthropic`).
85
+ `ChatAnthropic` requires the `anthropic` package: `pip install "chatlas[anthropic]"`.
86
86
  :::
87
87
 
88
88
  Examples
@@ -164,7 +164,7 @@ def ChatAnthropic(
164
164
  """
165
165
 
166
166
  if model is None:
167
- model = log_model_default("claude-3-5-sonnet-latest")
167
+ model = log_model_default("claude-3-7-sonnet-latest")
168
168
 
169
169
  return Chat(
170
170
  provider=AnthropicProvider(
@@ -572,8 +572,8 @@ def ChatBedrockAnthropic(
572
572
  ::: {.callout-note}
573
573
  ## Python requirements
574
574
 
575
- `ChatBedrockAnthropic`, requires the `anthropic` package with the `bedrock` extras
576
- (e.g., `pip install anthropic[bedrock]`).
575
+ `ChatBedrockAnthropic`, requires the `anthropic` package with the `bedrock` extras:
576
+ `pip install "chatlas[bedrock-anthropic]"`
577
577
  :::
578
578
 
579
579
  Examples
chatlas/_auto.py ADDED
@@ -0,0 +1,183 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ from typing import Callable, Literal, Optional
6
+
7
+ from ._anthropic import ChatAnthropic, ChatBedrockAnthropic
8
+ from ._chat import Chat
9
+ from ._github import ChatGithub
10
+ from ._google import ChatGoogle, ChatVertex
11
+ from ._groq import ChatGroq
12
+ from ._ollama import ChatOllama
13
+ from ._openai import ChatAzureOpenAI, ChatOpenAI
14
+ from ._perplexity import ChatPerplexity
15
+ from ._snowflake import ChatSnowflake
16
+ from ._turn import Turn
17
+
18
+ AutoProviders = Literal[
19
+ "anthropic",
20
+ "bedrock-anthropic",
21
+ "github",
22
+ "google",
23
+ "groq",
24
+ "ollama",
25
+ "openai",
26
+ "azure-openai",
27
+ "perplexity",
28
+ "snowflake",
29
+ "vertex",
30
+ ]
31
+
32
+ _provider_chat_model_map: dict[AutoProviders, Callable[..., Chat]] = {
33
+ "anthropic": ChatAnthropic,
34
+ "bedrock-anthropic": ChatBedrockAnthropic,
35
+ "github": ChatGithub,
36
+ "google": ChatGoogle,
37
+ "groq": ChatGroq,
38
+ "ollama": ChatOllama,
39
+ "openai": ChatOpenAI,
40
+ "azure-openai": ChatAzureOpenAI,
41
+ "perplexity": ChatPerplexity,
42
+ "snowflake": ChatSnowflake,
43
+ "vertex": ChatVertex,
44
+ }
45
+
46
+
47
+ def ChatAuto(
48
+ system_prompt: Optional[str] = None,
49
+ turns: Optional[list[Turn]] = None,
50
+ *,
51
+ provider: Optional[AutoProviders] = None,
52
+ model: Optional[str] = None,
53
+ **kwargs,
54
+ ) -> Chat:
55
+ """
56
+ Use environment variables (env vars) to configure the Chat provider and model.
57
+
58
+ Creates a `:class:~chatlas.Chat` instance based on the specified provider.
59
+ The provider may be specified through the `provider` parameter and/or the
60
+ `CHATLAS_CHAT_PROVIDER` env var. If both are set, the env var takes
61
+ precedence. Similarly, the provider's model may be specified through the
62
+ `model` parameter and/or the `CHATLAS_CHAT_MODEL` env var. Also, additional
63
+ configuration may be provided through the `kwargs` parameter and/or the
64
+ `CHATLAS_CHAT_ARGS` env var (as a JSON string). In this case, when both are
65
+ set, they are merged, with the env var arguments taking precedence.
66
+
67
+ As a result, `ChatAuto()` provides a convenient way to set a default
68
+ provider and model in your Python code, while allowing you to override
69
+ these settings through env vars (i.e., without modifying your code).
70
+
71
+ Prerequisites
72
+ -------------
73
+
74
+ ::: {.callout-note}
75
+ ## API key
76
+
77
+ Follow the instructions for the specific provider to obtain an API key.
78
+ :::
79
+
80
+ ::: {.callout-note}
81
+ ## Python requirements
82
+
83
+ Follow the instructions for the specific provider to install the required
84
+ Python packages.
85
+ :::
86
+
87
+
88
+ Examples
89
+ --------
90
+ First, set the environment variables for the provider, arguments, and API key:
91
+
92
+ ```bash
93
+ export CHATLAS_CHAT_PROVIDER=anthropic
94
+ export CHATLAS_CHAT_MODEL=claude-3-haiku-20240229
95
+ export CHATLAS_CHAT_ARGS='{"kwargs": {"max_retries": 3}}'
96
+ export ANTHROPIC_API_KEY=your_api_key
97
+ ```
98
+
99
+ Then, you can use the `ChatAuto` function to create a Chat instance:
100
+
101
+ ```python
102
+ from chatlas import ChatAuto
103
+
104
+ chat = ChatAuto()
105
+ chat.chat("What is the capital of France?")
106
+ ```
107
+
108
+ Parameters
109
+ ----------
110
+ provider
111
+ The name of the default chat provider to use. Providers are strings
112
+ formatted in kebab-case, e.g. to use `ChatBedrockAnthropic` set
113
+ `provider="bedrock-anthropic"`.
114
+
115
+ This value can also be provided via the `CHATLAS_CHAT_PROVIDER`
116
+ environment variable, which takes precedence over `provider`
117
+ when set.
118
+ model
119
+ The name of the default model to use. This value can also be provided
120
+ via the `CHATLAS_CHAT_MODEL` environment variable, which takes
121
+ precedence over `model` when set.
122
+ system_prompt
123
+ A system prompt to set the behavior of the assistant.
124
+ turns
125
+ A list of turns to start the chat with (i.e., continuing a previous
126
+ conversation). If not provided, the conversation begins from scratch. Do
127
+ not provide non-`None` values for both `turns` and `system_prompt`. Each
128
+ message in the list should be a dictionary with at least `role` (usually
129
+ `system`, `user`, or `assistant`, but `tool` is also possible). Normally
130
+ there is also a `content` field, which is a string.
131
+ **kwargs
132
+ Additional keyword arguments to pass to the Chat constructor. See the
133
+ documentation for each provider for more details on the available
134
+ options.
135
+
136
+ These arguments can also be provided via the `CHATLAS_CHAT_ARGS`
137
+ environment variable as a JSON string. When provided, the options
138
+ in the `CHATLAS_CHAT_ARGS` envvar take precedence over the options
139
+ passed to `kwargs`.
140
+
141
+ Note that `system_prompt` and `turns` in `kwargs` or in
142
+ `CHATLAS_CHAT_ARGS` are ignored.
143
+
144
+ Returns
145
+ -------
146
+ Chat
147
+ A chat instance using the specified provider.
148
+
149
+ Raises
150
+ ------
151
+ ValueError
152
+ If no valid provider is specified either through parameters or
153
+ environment variables.
154
+ """
155
+ the_provider = os.environ.get("CHATLAS_CHAT_PROVIDER", provider)
156
+
157
+ if the_provider is None:
158
+ raise ValueError(
159
+ "Provider name is required as parameter or `CHATLAS_CHAT_PROVIDER` must be set."
160
+ )
161
+ if the_provider not in _provider_chat_model_map:
162
+ raise ValueError(
163
+ f"Provider name '{the_provider}' is not a known chatlas provider: "
164
+ f"{', '.join(_provider_chat_model_map.keys())}"
165
+ )
166
+
167
+ # `system_prompt` and `turns` always come from `ChatAuto()`
168
+ base_args = {"system_prompt": system_prompt, "turns": turns}
169
+
170
+ if env_model := os.environ.get("CHATLAS_CHAT_MODEL"):
171
+ model = env_model
172
+
173
+ if model:
174
+ base_args["model"] = model
175
+
176
+ env_kwargs = {}
177
+ if env_kwargs_str := os.environ.get("CHATLAS_CHAT_ARGS"):
178
+ env_kwargs = json.loads(env_kwargs_str)
179
+
180
+ kwargs = {**kwargs, **env_kwargs, **base_args}
181
+ kwargs = {k: v for k, v in kwargs.items() if v is not None}
182
+
183
+ return _provider_chat_model_map[the_provider](**kwargs)
chatlas/_chat.py CHANGED
@@ -408,7 +408,9 @@ class Chat(Generic[SubmitInputArgsT, CompletionT]):
408
408
  Whether to run the app in a background thread. If `None`, the app will
409
409
  run in a background thread if the current environment is a notebook.
410
410
  echo
411
- Whether to echo text content, all content (i.e., tool calls), or no content. Defaults to `"none"` when `stream=True` and `"text"` when `stream=False`.
411
+ Whether to echo text content, all content (i.e., tool calls), or no
412
+ content. Defaults to `"none"` when `stream=True` and `"text"` when
413
+ `stream=False`.
412
414
  kwargs
413
415
  Additional keyword arguments to pass to the method used for requesting
414
416
  the response.
chatlas/_content.py CHANGED
@@ -199,21 +199,24 @@ class ContentToolResult(Content):
199
199
  name: Optional[str] = None
200
200
  error: Optional[str] = None
201
201
 
202
- def _get_value_and_language(self) -> tuple[str, str]:
202
+ def _get_value(self, pretty: bool = False) -> str:
203
203
  if self.error:
204
- return f"Tool calling failed with error: '{self.error}'", ""
204
+ return f"Tool calling failed with error: '{self.error}'"
205
+ if not pretty:
206
+ return str(self.value)
205
207
  try:
206
- json_val = json.loads(self.value)
207
- return pformat(json_val, indent=2, sort_dicts=False), "python"
208
+ json_val = json.loads(self.value) # type: ignore
209
+ return pformat(json_val, indent=2, sort_dicts=False)
208
210
  except: # noqa: E722
209
- return str(self.value), ""
211
+ return str(self.value)
210
212
 
213
+ # Primarily used for `echo="all"`...
211
214
  def __str__(self):
212
215
  comment = f"# tool result ({self.id})"
213
- value, language = self._get_value_and_language()
214
-
215
- return f"""```{language}\n{comment}\n{value}\n```"""
216
+ value = self._get_value(pretty=True)
217
+ return f"""```python\n{comment}\n{value}\n```"""
216
218
 
219
+ # ... and for displaying in the notebook
217
220
  def _repr_markdown_(self):
218
221
  return self.__str__()
219
222
 
@@ -224,9 +227,9 @@ class ContentToolResult(Content):
224
227
  res += f" error='{self.error}'"
225
228
  return res + ">"
226
229
 
230
+ # The actual value to send to the model
227
231
  def get_final_value(self) -> str:
228
- value, _language = self._get_value_and_language()
229
- return value
232
+ return self._get_value()
230
233
 
231
234
 
232
235
  @dataclass
chatlas/_display.py CHANGED
@@ -6,6 +6,7 @@ from uuid import uuid4
6
6
  from rich.live import Live
7
7
  from rich.logging import RichHandler
8
8
 
9
+ from ._live_render import LiveRender
9
10
  from ._logging import logger
10
11
  from ._typing_extensions import TypedDict
11
12
 
@@ -44,13 +45,22 @@ class LiveMarkdownDisplay(MarkdownDisplay):
44
45
  from rich.console import Console
45
46
 
46
47
  self.content: str = ""
47
- self.live = Live(
48
+ live = Live(
48
49
  auto_refresh=False,
49
- vertical_overflow="visible",
50
50
  console=Console(
51
51
  **echo_options["rich_console"],
52
52
  ),
53
53
  )
54
+
55
+ # Monkeypatch LiveRender() with our own version that add "crop_above"
56
+ # https://github.com/Textualize/rich/blob/43d3b047/rich/live.py#L87-L89
57
+ live.vertical_overflow = "crop_above"
58
+ live._live_render = LiveRender( # pyright: ignore[reportAttributeAccessIssue]
59
+ live.get_renderable(), vertical_overflow="crop_above"
60
+ )
61
+
62
+ self.live = live
63
+
54
64
  self._markdown_options = echo_options["rich_markdown"]
55
65
 
56
66
  def update(self, content: str):
chatlas/_github.py CHANGED
@@ -43,7 +43,7 @@ def ChatGithub(
43
43
  ::: {.callout-note}
44
44
  ## Python requirements
45
45
 
46
- `ChatGithub` requires the `openai` package (e.g., `pip install openai`).
46
+ `ChatGithub` requires the `openai` package: `pip install "chatlas[github]"`.
47
47
  :::
48
48
 
49
49
 
chatlas/_google.py CHANGED
@@ -61,8 +61,7 @@ def ChatGoogle(
61
61
  ::: {.callout-note}
62
62
  ## Python requirements
63
63
 
64
- `ChatGoogle` requires the `google-genai` package
65
- (e.g., `pip install google-genai`).
64
+ `ChatGoogle` requires the `google-genai` package: `pip install "chatlas[google]"`.
66
65
  :::
67
66
 
68
67
  Examples
@@ -413,7 +412,7 @@ class GoogleProvider(
413
412
  elif isinstance(content, ContentToolRequest):
414
413
  return Part(
415
414
  function_call=FunctionCall(
416
- id=content.id,
415
+ id=content.id if content.name != content.id else None,
417
416
  name=content.name,
418
417
  # Goes in a dict, so should come out as a dict
419
418
  args=cast(dict[str, Any], content.arguments),
@@ -428,7 +427,7 @@ class GoogleProvider(
428
427
  # TODO: seems function response parts might need role='tool'???
429
428
  # https://github.com/googleapis/python-genai/blame/c8cfef85c/README.md#L344
430
429
  function_response=FunctionResponse(
431
- id=content.id,
430
+ id=content.id if content.name != content.id else None,
432
431
  name=content.name,
433
432
  response=resp,
434
433
  )
@@ -530,8 +529,7 @@ def ChatVertex(
530
529
  ::: {.callout-note}
531
530
  ## Python requirements
532
531
 
533
- `ChatGoogle` requires the `google-genai` package
534
- (e.g., `pip install google-genai`).
532
+ `ChatGoogle` requires the `google-genai` package: `pip install "chatlas[vertex]"`.
535
533
  :::
536
534
 
537
535
  ::: {.callout-note}
chatlas/_groq.py CHANGED
@@ -41,7 +41,7 @@ def ChatGroq(
41
41
  ::: {.callout-note}
42
42
  ## Python requirements
43
43
 
44
- `ChatGroq` requires the `openai` package (e.g., `pip install openai`).
44
+ `ChatGroq` requires the `openai` package: `pip install "chatlas[groq]"`.
45
45
  :::
46
46
 
47
47
  Examples
@@ -0,0 +1,116 @@
1
+ # A 'patched' version of LiveRender that adds the 'crop_above' vertical overflow method.
2
+ # Derives from https://github.com/Textualize/rich/pull/3637
3
+ import sys
4
+ from typing import Optional, Tuple
5
+
6
+ if sys.version_info >= (3, 8):
7
+ from typing import Literal
8
+ else:
9
+ from typing_extensions import Literal # pragma: no cover
10
+
11
+ from rich._loop import loop_last
12
+ from rich.console import Console, ConsoleOptions, RenderableType, RenderResult
13
+ from rich.control import Control
14
+ from rich.segment import ControlType, Segment
15
+ from rich.style import StyleType
16
+ from rich.text import Text
17
+
18
+ VerticalOverflowMethod = Literal["crop", "crop_above", "ellipsis", "visible"]
19
+
20
+
21
+ class LiveRender:
22
+ """Creates a renderable that may be updated.
23
+
24
+ Args:
25
+ renderable (RenderableType): Any renderable object.
26
+ style (StyleType, optional): An optional style to apply to the renderable. Defaults to "".
27
+ """
28
+
29
+ def __init__(
30
+ self,
31
+ renderable: RenderableType,
32
+ style: StyleType = "",
33
+ vertical_overflow: VerticalOverflowMethod = "ellipsis",
34
+ ) -> None:
35
+ self.renderable = renderable
36
+ self.style = style
37
+ self.vertical_overflow = vertical_overflow
38
+ self._shape: Optional[Tuple[int, int]] = None
39
+
40
+ def set_renderable(self, renderable: RenderableType) -> None:
41
+ """Set a new renderable.
42
+
43
+ Args:
44
+ renderable (RenderableType): Any renderable object, including str.
45
+ """
46
+ self.renderable = renderable
47
+
48
+ def position_cursor(self) -> Control:
49
+ """Get control codes to move cursor to beginning of live render.
50
+
51
+ Returns:
52
+ Control: A control instance that may be printed.
53
+ """
54
+ if self._shape is not None:
55
+ _, height = self._shape
56
+ return Control(
57
+ ControlType.CARRIAGE_RETURN,
58
+ (ControlType.ERASE_IN_LINE, 2),
59
+ *(
60
+ (
61
+ (ControlType.CURSOR_UP, 1),
62
+ (ControlType.ERASE_IN_LINE, 2),
63
+ )
64
+ * (height - 1)
65
+ ),
66
+ )
67
+ return Control()
68
+
69
+ def restore_cursor(self) -> Control:
70
+ """Get control codes to clear the render and restore the cursor to its previous position.
71
+
72
+ Returns:
73
+ Control: A Control instance that may be printed.
74
+ """
75
+ if self._shape is not None:
76
+ _, height = self._shape
77
+ return Control(
78
+ ControlType.CARRIAGE_RETURN,
79
+ *((ControlType.CURSOR_UP, 1), (ControlType.ERASE_IN_LINE, 2)) * height,
80
+ )
81
+ return Control()
82
+
83
+ def __rich_console__(
84
+ self, console: Console, options: ConsoleOptions
85
+ ) -> RenderResult:
86
+ renderable = self.renderable
87
+ style = console.get_style(self.style)
88
+ lines = console.render_lines(renderable, options, style=style, pad=False)
89
+ shape = Segment.get_shape(lines)
90
+
91
+ _, height = shape
92
+ if height > options.size.height:
93
+ if self.vertical_overflow == "crop":
94
+ lines = lines[: options.size.height]
95
+ shape = Segment.get_shape(lines)
96
+ elif self.vertical_overflow == "crop_above":
97
+ lines = lines[-(options.size.height) :]
98
+ shape = Segment.get_shape(lines)
99
+ elif self.vertical_overflow == "ellipsis":
100
+ lines = lines[: (options.size.height - 1)]
101
+ overflow_text = Text(
102
+ "...",
103
+ overflow="crop",
104
+ justify="center",
105
+ end="",
106
+ style="live.ellipsis",
107
+ )
108
+ lines.append(list(console.render(overflow_text)))
109
+ shape = Segment.get_shape(lines)
110
+ self._shape = shape
111
+
112
+ new_line = Segment.line()
113
+ for last, line in loop_last(lines):
114
+ yield from line
115
+ if not last:
116
+ yield new_line
chatlas/_ollama.py CHANGED
@@ -51,7 +51,7 @@ def ChatOllama(
51
51
  ::: {.callout-note}
52
52
  ## Python requirements
53
53
 
54
- `ChatOllama` requires the `openai` package (e.g., `pip install openai`).
54
+ `ChatOllama` requires the `openai` package: `pip install "chatlas[ollama]"`.
55
55
  :::
56
56
 
57
57
 
chatlas/_openai.py CHANGED
@@ -79,7 +79,7 @@ def ChatOpenAI(
79
79
  ::: {.callout-note}
80
80
  ## Python requirements
81
81
 
82
- `ChatOpenAI` requires the `openai` package (e.g., `pip install openai`).
82
+ `ChatOpenAI` requires the `openai` package: `pip install "chatlas[openai]"`.
83
83
  :::
84
84
 
85
85
  Examples
@@ -592,7 +592,8 @@ def ChatAzureOpenAI(
592
592
  ::: {.callout-note}
593
593
  ## Python requirements
594
594
 
595
- `ChatAzureOpenAI` requires the `openai` package (e.g., `pip install openai`).
595
+ `ChatAzureOpenAI` requires the `openai` package:
596
+ `pip install "chatlas[azure-openai]"`.
596
597
  :::
597
598
 
598
599
  Examples
chatlas/_perplexity.py CHANGED
@@ -43,7 +43,7 @@ def ChatPerplexity(
43
43
  ::: {.callout-note}
44
44
  ## Python requirements
45
45
 
46
- `ChatPerplexity` requires the `openai` package (e.g., `pip install openai`).
46
+ `ChatPerplexity` requires the `openai` package: `pip install "chatlas[perplexity]"`.
47
47
  :::
48
48
 
49
49
 
chatlas/_snowflake.py ADDED
@@ -0,0 +1,321 @@
1
+ from typing import TYPE_CHECKING, Literal, Optional, TypedDict, overload
2
+
3
+ from pydantic import BaseModel
4
+
5
+ from ._chat import Chat
6
+ from ._content import Content
7
+ from ._logging import log_model_default
8
+ from ._provider import Provider
9
+ from ._tools import Tool
10
+ from ._turn import Turn, normalize_turns
11
+ from ._utils import drop_none
12
+
13
+ if TYPE_CHECKING:
14
+ from snowflake.snowpark import Column
15
+
16
+ # Types inferred from the return type of the `snowflake.cortex.complete` function
17
+ Completion = str | Column
18
+ CompletionChunk = str
19
+
20
+ from .types.snowflake import SubmitInputArgs
21
+
22
+
23
+ # The main prompt input type for Snowflake
24
+ # This was copy-pasted from `snowflake.cortex._complete.ConversationMessage`
25
+ class ConversationMessage(TypedDict):
26
+ role: str
27
+ content: str
28
+
29
+
30
+ def ChatSnowflake(
31
+ *,
32
+ system_prompt: Optional[str] = None,
33
+ model: Optional[str] = None,
34
+ turns: Optional[list[Turn]] = None,
35
+ connection_name: Optional[str] = None,
36
+ account: Optional[str] = None,
37
+ user: Optional[str] = None,
38
+ password: Optional[str] = None,
39
+ private_key_file: Optional[str] = None,
40
+ private_key_file_pwd: Optional[str] = None,
41
+ kwargs: Optional[dict[str, "str | int"]] = None,
42
+ ) -> Chat["SubmitInputArgs", "Completion"]:
43
+ """
44
+ Chat with a Snowflake Cortex LLM
45
+
46
+ https://docs.snowflake.com/en/user-guide/snowflake-cortex/llm-functions
47
+
48
+ Prerequisites
49
+ -------------
50
+
51
+ ::: {.callout-note}
52
+ ## Python requirements
53
+
54
+ `ChatSnowflake`, requires the `snowflake-ml-python` package:
55
+ `pip install "chatlas[snowflake]"`.
56
+ :::
57
+
58
+ ::: {.callout-note}
59
+ ## Snowflake credentials
60
+
61
+ Snowflake provides a handful of ways to authenticate, but it's recommended
62
+ to use [key-pair
63
+ auth](https://docs.snowflake.com/en/developer-guide/python-connector/python-connector-connect#label-python-connection-toml)
64
+ to generate a `private_key_file`. It's also recommended to place your
65
+ credentials in a [`connections.toml`
66
+ file](https://docs.snowflake.com/en/developer-guide/snowpark/python/creating-session#connect-by-using-the-connections-toml-file).
67
+
68
+ This way, once your credentials are in the `connections.toml` file, you can
69
+ simply call `ChatSnowflake(connection_name="my_connection")` to
70
+ authenticate. If you don't want to use a `connections.toml` file, you can
71
+ specify the connection parameters directly (with `account`, `user`,
72
+ `password`, etc.).
73
+ :::
74
+
75
+
76
+ Parameters
77
+ ----------
78
+ system_prompt
79
+ A system prompt to set the behavior of the assistant.
80
+ model
81
+ The model to use for the chat. The default, None, will pick a reasonable
82
+ default, and warn you about it. We strongly recommend explicitly
83
+ choosing a model for all but the most casual use.
84
+ turns
85
+ A list of turns to start the chat with (i.e., continuing a previous
86
+ conversation). If not provided, the conversation begins from scratch. Do
87
+ not provide non-None values for both `turns` and `system_prompt`. Each
88
+ message in the list should be a dictionary with at least `role` (usually
89
+ `system`, `user`, or `assistant`, but `tool` is also possible). Normally
90
+ there is also a `content` field, which is a string.
91
+ connection_name
92
+ The name of the connection (i.e., section) within the connections.toml file.
93
+ This is useful if you want to keep your credentials in a connections.toml file
94
+ rather than specifying them directly in the arguments.
95
+ https://docs.snowflake.com/en/developer-guide/snowpark/python/creating-session#connect-by-using-the-connections-toml-file
96
+ account
97
+ Your Snowflake account identifier. Required if `connection_name` is not provided.
98
+ https://docs.snowflake.com/en/user-guide/admin-account-identifier
99
+ user
100
+ Your Snowflake user name. Required if `connection_name` is not provided.
101
+ password
102
+ Your Snowflake password. Required if doing password authentication and
103
+ `connection_name` is not provided.
104
+ private_key_file
105
+ The path to your private key file. Required if you are using key pair authentication.
106
+ https://docs.snowflake.com/en/user-guide/key-pair-auth
107
+ private_key_file_pwd
108
+ The password for your private key file. Required if you are using key pair authentication.
109
+ https://docs.snowflake.com/en/user-guide/key-pair-auth
110
+ kwargs
111
+ Additional keyword arguments passed along to the Snowflake connection builder. These can
112
+ include any parameters supported by the `snowflake-ml-python` package.
113
+ https://docs.snowflake.com/en/developer-guide/snowpark/python/creating-session#connect-by-specifying-connection-parameters
114
+ """
115
+
116
+ if model is None:
117
+ model = log_model_default("llama3.1-70b")
118
+
119
+ return Chat(
120
+ provider=SnowflakeProvider(
121
+ model=model,
122
+ connection_name=connection_name,
123
+ account=account,
124
+ user=user,
125
+ password=password,
126
+ private_key_file=private_key_file,
127
+ private_key_file_pwd=private_key_file_pwd,
128
+ kwargs=kwargs,
129
+ ),
130
+ turns=normalize_turns(
131
+ turns or [],
132
+ system_prompt,
133
+ ),
134
+ )
135
+
136
+
137
+ class SnowflakeProvider(Provider["Completion", "CompletionChunk", "CompletionChunk"]):
138
+ def __init__(
139
+ self,
140
+ *,
141
+ model: str,
142
+ connection_name: Optional[str],
143
+ account: Optional[str],
144
+ user: Optional[str],
145
+ password: Optional[str],
146
+ private_key_file: Optional[str],
147
+ private_key_file_pwd: Optional[str],
148
+ kwargs: Optional[dict[str, "str | int"]],
149
+ ):
150
+ try:
151
+ from snowflake.snowpark import Session
152
+ except ImportError:
153
+ raise ImportError(
154
+ "`ChatSnowflake()` requires the `snowflake-ml-python` package. "
155
+ "Please install it via `pip install snowflake-ml-python`."
156
+ )
157
+
158
+ configs: dict[str, str | int] = drop_none(
159
+ {
160
+ "connection_name": connection_name,
161
+ "account": account,
162
+ "user": user,
163
+ "password": password,
164
+ "private_key_file": private_key_file,
165
+ "private_key_file_pwd": private_key_file_pwd,
166
+ **(kwargs or {}),
167
+ }
168
+ )
169
+
170
+ self._model = model
171
+ self._session = Session.builder.configs(configs).create()
172
+
173
+ @overload
174
+ def chat_perform(
175
+ self,
176
+ *,
177
+ stream: Literal[False],
178
+ turns: list[Turn],
179
+ tools: dict[str, Tool],
180
+ data_model: Optional[type[BaseModel]] = None,
181
+ kwargs: Optional["SubmitInputArgs"] = None,
182
+ ): ...
183
+
184
+ @overload
185
+ def chat_perform(
186
+ self,
187
+ *,
188
+ stream: Literal[True],
189
+ turns: list[Turn],
190
+ tools: dict[str, Tool],
191
+ data_model: Optional[type[BaseModel]] = None,
192
+ kwargs: Optional["SubmitInputArgs"] = None,
193
+ ): ...
194
+
195
+ def chat_perform(
196
+ self,
197
+ *,
198
+ stream: bool,
199
+ turns: list[Turn],
200
+ tools: dict[str, Tool],
201
+ data_model: Optional[type[BaseModel]] = None,
202
+ kwargs: Optional["SubmitInputArgs"] = None,
203
+ ):
204
+ from snowflake.cortex import complete
205
+
206
+ kwargs = self._chat_perform_args(stream, turns, tools, data_model, kwargs)
207
+ return complete(**kwargs)
208
+
209
+ @overload
210
+ async def chat_perform_async(
211
+ self,
212
+ *,
213
+ stream: Literal[False],
214
+ turns: list[Turn],
215
+ tools: dict[str, Tool],
216
+ data_model: Optional[type[BaseModel]] = None,
217
+ kwargs: Optional["SubmitInputArgs"] = None,
218
+ ): ...
219
+
220
+ @overload
221
+ async def chat_perform_async(
222
+ self,
223
+ *,
224
+ stream: Literal[True],
225
+ turns: list[Turn],
226
+ tools: dict[str, Tool],
227
+ data_model: Optional[type[BaseModel]] = None,
228
+ kwargs: Optional["SubmitInputArgs"] = None,
229
+ ): ...
230
+
231
+ async def chat_perform_async(
232
+ self,
233
+ *,
234
+ stream: bool,
235
+ turns: list[Turn],
236
+ tools: dict[str, Tool],
237
+ data_model: Optional[type[BaseModel]] = None,
238
+ kwargs: Optional["SubmitInputArgs"] = None,
239
+ ):
240
+ raise NotImplementedError(
241
+ "Snowflake does not currently support async completions."
242
+ )
243
+
244
+ def _chat_perform_args(
245
+ self,
246
+ stream: bool,
247
+ turns: list[Turn],
248
+ tools: dict[str, Tool],
249
+ data_model: Optional[type[BaseModel]] = None,
250
+ kwargs: Optional["SubmitInputArgs"] = None,
251
+ ):
252
+ # Cortex doesn't seem to support tools
253
+ if tools:
254
+ raise ValueError("Snowflake does not currently support tools.")
255
+
256
+ # TODO: implement data_model when this PR makes it into snowflake-ml-python
257
+ # https://github.com/snowflakedb/snowflake-ml-python/pull/141
258
+ # https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-llm-rest-api#structured-output-example
259
+ if data_model:
260
+ raise NotImplementedError(
261
+ "The snowflake-ml-python package currently doesn't support structured output. "
262
+ "Upvote this PR to help prioritize it: "
263
+ "https://github.com/snowflakedb/snowflake-ml-python/pull/141"
264
+ )
265
+
266
+ kwargs_full: "SubmitInputArgs" = {
267
+ "stream": stream,
268
+ "prompt": self._as_prompt_input(turns),
269
+ "model": self._model,
270
+ **(kwargs or {}),
271
+ }
272
+
273
+ return kwargs_full
274
+
275
+ def stream_text(self, chunk):
276
+ return chunk
277
+
278
+ def stream_merge_chunks(self, completion, chunk):
279
+ if completion is None:
280
+ return chunk
281
+ return completion + chunk
282
+
283
+ def stream_turn(self, completion, has_data_model) -> Turn:
284
+ return self._as_turn(completion, has_data_model)
285
+
286
+ def value_turn(self, completion, has_data_model) -> Turn:
287
+ return self._as_turn(completion, has_data_model)
288
+
289
+ def token_count(
290
+ self,
291
+ *args: "Content | str",
292
+ tools: dict[str, Tool],
293
+ data_model: Optional[type[BaseModel]],
294
+ ) -> int:
295
+ raise NotImplementedError(
296
+ "Snowflake does not currently support token counting."
297
+ )
298
+
299
+ async def token_count_async(
300
+ self,
301
+ *args: "Content | str",
302
+ tools: dict[str, Tool],
303
+ data_model: Optional[type[BaseModel]],
304
+ ) -> int:
305
+ raise NotImplementedError(
306
+ "Snowflake does not currently support token counting."
307
+ )
308
+
309
+ def _as_prompt_input(self, turns: list[Turn]) -> list["ConversationMessage"]:
310
+ res: list["ConversationMessage"] = []
311
+ for turn in turns:
312
+ res.append(
313
+ {
314
+ "role": turn.role,
315
+ "content": turn.text,
316
+ }
317
+ )
318
+ return res
319
+
320
+ def _as_turn(self, completion, has_data_model) -> Turn:
321
+ return Turn("assistant", completion)
chatlas/_utils.py CHANGED
@@ -61,6 +61,13 @@ def is_async_callable(
61
61
  return False
62
62
 
63
63
 
64
+ T = TypeVar("T")
65
+
66
+
67
+ def drop_none(x: dict[str, T | None]) -> dict[str, T]:
68
+ return {k: v for k, v in x.items() if v is not None}
69
+
70
+
64
71
  # https://docs.pytest.org/en/latest/example/simple.html#pytest-current-test-environment-variable
65
72
  def is_testing():
66
73
  return os.environ.get("PYTEST_CURRENT_TEST", None) is not None
chatlas/_version.py ADDED
@@ -0,0 +1,21 @@
1
+ # file generated by setuptools-scm
2
+ # don't change, don't track in version control
3
+
4
+ __all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
5
+
6
+ TYPE_CHECKING = False
7
+ if TYPE_CHECKING:
8
+ from typing import Tuple
9
+ from typing import Union
10
+
11
+ VERSION_TUPLE = Tuple[Union[int, str], ...]
12
+ else:
13
+ VERSION_TUPLE = object
14
+
15
+ version: str
16
+ __version__: str
17
+ __version_tuple__: VERSION_TUPLE
18
+ version_tuple: VERSION_TUPLE
19
+
20
+ __version__ = version = '0.5.0'
21
+ __version_tuple__ = version_tuple = (0, 5, 0)
@@ -8,10 +8,15 @@ from typing import Iterable, Literal, Mapping, Optional, TypedDict, Union
8
8
  import anthropic
9
9
  import anthropic.types.message_param
10
10
  import anthropic.types.text_block_param
11
+ import anthropic.types.thinking_config_disabled_param
12
+ import anthropic.types.thinking_config_enabled_param
13
+ import anthropic.types.tool_bash_20250124_param
11
14
  import anthropic.types.tool_choice_any_param
12
15
  import anthropic.types.tool_choice_auto_param
16
+ import anthropic.types.tool_choice_none_param
13
17
  import anthropic.types.tool_choice_tool_param
14
18
  import anthropic.types.tool_param
19
+ import anthropic.types.tool_text_editor_20250124_param
15
20
 
16
21
 
17
22
  class SubmitInputArgs(TypedDict, total=False):
@@ -19,6 +24,8 @@ class SubmitInputArgs(TypedDict, total=False):
19
24
  messages: Iterable[anthropic.types.message_param.MessageParam]
20
25
  model: Union[
21
26
  Literal[
27
+ "claude-3-7-sonnet-latest",
28
+ "claude-3-7-sonnet-20250219",
22
29
  "claude-3-5-haiku-latest",
23
30
  "claude-3-5-haiku-20241022",
24
31
  "claude-3-5-sonnet-latest",
@@ -41,13 +48,28 @@ class SubmitInputArgs(TypedDict, total=False):
41
48
  anthropic.NotGiven,
42
49
  ]
43
50
  temperature: float | anthropic.NotGiven
51
+ thinking: Union[
52
+ anthropic.types.thinking_config_enabled_param.ThinkingConfigEnabledParam,
53
+ anthropic.types.thinking_config_disabled_param.ThinkingConfigDisabledParam,
54
+ anthropic.NotGiven,
55
+ ]
44
56
  tool_choice: Union[
45
57
  anthropic.types.tool_choice_auto_param.ToolChoiceAutoParam,
46
58
  anthropic.types.tool_choice_any_param.ToolChoiceAnyParam,
47
59
  anthropic.types.tool_choice_tool_param.ToolChoiceToolParam,
60
+ anthropic.types.tool_choice_none_param.ToolChoiceNoneParam,
61
+ anthropic.NotGiven,
62
+ ]
63
+ tools: Union[
64
+ Iterable[
65
+ Union[
66
+ anthropic.types.tool_param.ToolParam,
67
+ anthropic.types.tool_bash_20250124_param.ToolBash20250124Param,
68
+ anthropic.types.tool_text_editor_20250124_param.ToolTextEditor20250124Param,
69
+ ]
70
+ ],
48
71
  anthropic.NotGiven,
49
72
  ]
50
- tools: Union[Iterable[anthropic.types.tool_param.ToolParam], anthropic.NotGiven]
51
73
  top_k: int | anthropic.NotGiven
52
74
  top_p: float | anthropic.NotGiven
53
75
  extra_headers: Optional[Mapping[str, Union[str, anthropic.Omit]]]
@@ -46,6 +46,11 @@ class SubmitInputArgs(TypedDict, total=False):
46
46
  "o1-preview-2024-09-12",
47
47
  "o1-mini",
48
48
  "o1-mini-2024-09-12",
49
+ "computer-use-preview",
50
+ "computer-use-preview-2025-02-04",
51
+ "computer-use-preview-2025-03-11",
52
+ "gpt-4.5-preview",
53
+ "gpt-4.5-preview-2025-02-27",
49
54
  "gpt-4o",
50
55
  "gpt-4o-2024-11-20",
51
56
  "gpt-4o-2024-08-06",
@@ -110,8 +115,8 @@ class SubmitInputArgs(TypedDict, total=False):
110
115
  reasoning_effort: Union[Literal["low", "medium", "high"], None, openai.NotGiven]
111
116
  response_format: Union[
112
117
  openai.types.shared_params.response_format_text.ResponseFormatText,
113
- openai.types.shared_params.response_format_json_object.ResponseFormatJSONObject,
114
118
  openai.types.shared_params.response_format_json_schema.ResponseFormatJSONSchema,
119
+ openai.types.shared_params.response_format_json_object.ResponseFormatJSONObject,
115
120
  openai.NotGiven,
116
121
  ]
117
122
  seed: Union[int, None, openai.NotGiven]
@@ -0,0 +1,8 @@
1
+ # ---------------------------------------------------------
2
+ # Do not modify this file. It was generated by `scripts/generate_typed_dicts.py`.
3
+ # ---------------------------------------------------------
4
+
5
+
6
+ from ._submit import SubmitInputArgs
7
+
8
+ __all__ = ("SubmitInputArgs",)
@@ -0,0 +1,24 @@
1
+ # ---------------------------------------------------------
2
+ # Do not modify this file. It was generated by `scripts/generate_typed_dicts.py`.
3
+ # ---------------------------------------------------------
4
+
5
+
6
+ from typing import Optional, TypedDict, Union
7
+
8
+ import snowflake.cortex._complete
9
+ import snowflake.snowpark.column
10
+ import snowflake.snowpark.session
11
+
12
+
13
+ class SubmitInputArgs(TypedDict, total=False):
14
+ model: Union[str, snowflake.snowpark.column.Column]
15
+ prompt: Union[
16
+ str,
17
+ list[snowflake.cortex._complete.ConversationMessage],
18
+ snowflake.snowpark.column.Column,
19
+ ]
20
+ options: Optional[snowflake.cortex._complete.CompleteOptions]
21
+ session: Optional[snowflake.snowpark.session.Session]
22
+ stream: bool
23
+ timeout: Optional[float]
24
+ deadline: Optional[float]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chatlas
3
- Version: 0.4.0
3
+ Version: 0.5.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
@@ -20,6 +20,12 @@ Requires-Python: >=3.9
20
20
  Requires-Dist: jinja2
21
21
  Requires-Dist: pydantic>=2.0
22
22
  Requires-Dist: rich
23
+ Provides-Extra: anthropic
24
+ Requires-Dist: anthropic; extra == 'anthropic'
25
+ Provides-Extra: azure-openai
26
+ Requires-Dist: openai; extra == 'azure-openai'
27
+ Provides-Extra: bedrock-anthropic
28
+ Requires-Dist: anthropic[bedrock]; extra == 'bedrock-anthropic'
23
29
  Provides-Extra: dev
24
30
  Requires-Dist: anthropic[bedrock]; extra == 'dev'
25
31
  Requires-Dist: google-genai>=1.2.0; extra == 'dev'
@@ -30,7 +36,9 @@ Requires-Dist: pillow; extra == 'dev'
30
36
  Requires-Dist: python-dotenv; extra == 'dev'
31
37
  Requires-Dist: ruff>=0.6.5; extra == 'dev'
32
38
  Requires-Dist: shiny; extra == 'dev'
39
+ Requires-Dist: snowflake-ml-python; extra == 'dev'
33
40
  Requires-Dist: tiktoken; extra == 'dev'
41
+ Requires-Dist: torch; (python_version <= '3.11') and extra == 'dev'
34
42
  Provides-Extra: docs
35
43
  Requires-Dist: griffe>=1; extra == 'docs'
36
44
  Requires-Dist: ipykernel; extra == 'docs'
@@ -42,11 +50,27 @@ Requires-Dist: pandas; extra == 'docs'
42
50
  Requires-Dist: pyyaml; extra == 'docs'
43
51
  Requires-Dist: quartodoc>=0.7; extra == 'docs'
44
52
  Requires-Dist: sentence-transformers; extra == 'docs'
53
+ Provides-Extra: github
54
+ Requires-Dist: openai; extra == 'github'
55
+ Provides-Extra: google
56
+ Requires-Dist: google-genai; extra == 'google'
57
+ Provides-Extra: groq
58
+ Requires-Dist: openai; extra == 'groq'
59
+ Provides-Extra: ollama
60
+ Requires-Dist: openai; extra == 'ollama'
61
+ Provides-Extra: openai
62
+ Requires-Dist: openai; extra == 'openai'
63
+ Provides-Extra: perplexity
64
+ Requires-Dist: openai; extra == 'perplexity'
65
+ Provides-Extra: snowflake
66
+ Requires-Dist: snowflake-ml-python; extra == 'snowflake'
45
67
  Provides-Extra: test
46
68
  Requires-Dist: pyright>=1.1.379; extra == 'test'
47
69
  Requires-Dist: pytest-asyncio; extra == 'test'
48
70
  Requires-Dist: pytest>=8.3.2; extra == 'test'
49
71
  Requires-Dist: syrupy>=4; extra == 'test'
72
+ Provides-Extra: vertex
73
+ Requires-Dist: google-genai; extra == 'vertex'
50
74
  Description-Content-Type: text/markdown
51
75
 
52
76
  <h1 class="unnumbered unlisted"> chatlas <a href="https://posit-dev.github.io/chatlas"><img src="docs/images/logo.png" align="right" height="138" alt="chatlas website" /></a> </h1>
@@ -98,6 +122,7 @@ It also supports the following enterprise cloud providers:
98
122
 
99
123
  * AWS Bedrock: [`ChatBedrockAnthropic()`](https://posit-dev.github.io/chatlas/reference/ChatBedrockAnthropic.html).
100
124
  * Azure OpenAI: [`ChatAzureOpenAI()`](https://posit-dev.github.io/chatlas/reference/ChatAzureOpenAI.html).
125
+ * Snowflake Cortex: [`ChatSnowflake()`](https://posit-dev.github.io/chatlas/reference/ChatSnowflake.html).
101
126
  * Vertex AI: [`ChatVertex()`](https://posit-dev.github.io/chatlas/reference/ChatVertex.html).
102
127
 
103
128
  To use a model provider that isn't listed here, you have two options:
@@ -0,0 +1,44 @@
1
+ chatlas/__init__.py,sha256=U-uNJWeVjMj4aSre2evTaVunabzLksZ5Toz19ygQB-o,1251
2
+ chatlas/_anthropic.py,sha256=-Qrq-8jgNIpW_temJYXKTb7VCl2ldnpe-KSX89L5Lhc,24264
3
+ chatlas/_auto.py,sha256=4tpwla09la4VA2PAh3phAMWs2Amgtp_4Qsjx6K02ib0,6032
4
+ chatlas/_chat.py,sha256=hS0sirnZBwMaOJ47wyNgdA61Xd7AJ_xVhj4v8RbLoHA,46014
5
+ chatlas/_content.py,sha256=L_ry6W5D2h6Eag2tZmqIbMP4OQj-vVXRircLcUtgQ6c,6454
6
+ chatlas/_content_image.py,sha256=4nk9wTvLtNmtcytdFp8p9otEV5-0_K6wzIxCyK0PIEI,8367
7
+ chatlas/_display.py,sha256=eqdRIwQenyJxswmTEjnJ1n9YxxSxsa8vHVmA79449_o,4439
8
+ chatlas/_github.py,sha256=8_vvUIBCprgrQ5UItky5yETfEQPG2fCMM57ga77p28E,4377
9
+ chatlas/_google.py,sha256=ehL8hf8Hogg9qK9OaXqiWainOirz21yy_lx_yvUwKf8,18831
10
+ chatlas/_groq.py,sha256=iuFvxeXkq81sDHxVV9zbVHjf2ZuNT94P-XkuXvqtGms,4160
11
+ chatlas/_interpolate.py,sha256=ykwLP3x-ya9Q33U4knSU75dtk6pzJAeythEEIW-43Pc,3631
12
+ chatlas/_live_render.py,sha256=UMZltE35LxziDKPMEeDwQ9meZ95SeqwhJi7j-y9pcro,4004
13
+ chatlas/_logging.py,sha256=7a20sAl1PkW1qBNrfd_ieUbQXV8Gf4Vuf0Wn62LNBmk,2290
14
+ chatlas/_merge.py,sha256=SGj_BetgA7gaOqSBKOhYmW3CYeQKTEehFrXvx3y4OYE,3924
15
+ chatlas/_ollama.py,sha256=EgTwmphVwBV7xCIqmPC_cNlr4Uo9N5Xy4eDCb1sJoPI,3764
16
+ chatlas/_openai.py,sha256=9KB4hEh6acFfBvLV0yXwLcjP-CyIpsMbbVt7eJ03_Vg,23870
17
+ chatlas/_perplexity.py,sha256=j-jfOIYefZC5XzGjmya9GCCGQN003cRmiAv6vmo0rTQ,4454
18
+ chatlas/_provider.py,sha256=YmdBbz_u5aP_kBxl6s26OPiSnWG_vZ_fvf9L2qvBmyI,3809
19
+ chatlas/_snowflake.py,sha256=WUNdT3irxgLVqoc1TAeDmxnYsjBWiBw-CoH-dY4mFps,10944
20
+ chatlas/_tokens.py,sha256=3W3EPUp9eWXUiwuzJwEPBv43AUznbK46pm59Htti7z4,2392
21
+ chatlas/_tokens_old.py,sha256=L9d9oafrXvEx2u4nIn_Jjn7adnQyLBnYBuPwJUE8Pl8,5005
22
+ chatlas/_tools.py,sha256=-qt4U1AFkebQoX9kpsBy5QXK8a2PpHX6Amgm44gcQ68,4113
23
+ chatlas/_turn.py,sha256=nKwk20FrOIrZX4xJxdGyUEpwUH2H-UYcoJLlO2ZD5iU,4836
24
+ chatlas/_typing_extensions.py,sha256=YdzmlyPSBpIEcsOkoz12e6jETT1XEMV2Q72haE4cfwY,1036
25
+ chatlas/_utils.py,sha256=2TPy5_8dr9QDF1YShZN-CjxRVHeArSujRiaF0SKnI4o,2895
26
+ chatlas/_version.py,sha256=N2U3TRgLfYxjXfoF4Dy9PxAbZq24zjCym3P3cwuxKP8,511
27
+ chatlas/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ chatlas/types/__init__.py,sha256=P_EDL4eqsigKwB-u2qRmKlYQS5Y65m7oWjGC3cYmxO4,719
29
+ chatlas/types/anthropic/__init__.py,sha256=OwubA-DPHYpYo0XyRyAFwftOI0mOxtHzAyhUSLcDx54,417
30
+ chatlas/types/anthropic/_client.py,sha256=G0LRhoFBcsSOMr5qhP-0rAScsVXaVlHCpggfVp54bnQ,690
31
+ chatlas/types/anthropic/_client_bedrock.py,sha256=mNazQlu0pQt8JdzrYn3LKNgE4n732GjhQUJdQQK9QkY,785
32
+ chatlas/types/anthropic/_submit.py,sha256=xoQyZ3SUUttWDPAjZTPfFch7D1bIU0AJNsenViYhAKs,2974
33
+ chatlas/types/google/__init__.py,sha256=ZJhi8Kwvio2zp8T1TQqmvdHqkS-Khb6BGESPjREADgo,337
34
+ chatlas/types/google/_client.py,sha256=t7aKbxYq_xOA1Z3RnWcjewifdQFSHi7vKEj6MyKMCJk,729
35
+ chatlas/types/google/_submit.py,sha256=b-ZqMvI551Ia7pFlWdqUQJjov3neHmVwLFw-P2bgU8w,1883
36
+ chatlas/types/openai/__init__.py,sha256=Q2RAr1bSH1nHsxICK05nAmKmxdhKmhbBkWD_XHiVSrI,411
37
+ chatlas/types/openai/_client.py,sha256=YGm_EHtRSSHeeOZe-CV7oNvMJpEblEta3UTuU7lSRO8,754
38
+ chatlas/types/openai/_client_azure.py,sha256=jx8D_p46CLDGzTP-k-TtGzj-f3junj6or-86m8DD_0w,858
39
+ chatlas/types/openai/_submit.py,sha256=pJ6_G5R3ouI17-20fvjv0pzs_VizTlMbrBx7GXiOpNs,6294
40
+ chatlas/types/snowflake/__init__.py,sha256=NVKw_gLVnSlMNdE6BpikrQw8GV8LvIn5SR8eI8Afgbs,273
41
+ chatlas/types/snowflake/_submit.py,sha256=Fgcb2Z4mXYwAR2b7Kn3SdEYFlO4gJiUvkDJ3lDoN0IY,799
42
+ chatlas-0.5.0.dist-info/METADATA,sha256=ObTTRN6dDvUuAWuW07VDvkvwbRbeMlH6AwRvLmVFDvM,14409
43
+ chatlas-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
+ chatlas-0.5.0.dist-info/RECORD,,
@@ -1,38 +0,0 @@
1
- chatlas/__init__.py,sha256=HtvcxTy7A3CO7d6JSZIGDqIPGBRuo7Vte11nazn_VNE,1004
2
- chatlas/_anthropic.py,sha256=fygtGcKi5qM1w40AZbcnJcsPUkBEyWuqpoyCOKG-kOc,24259
3
- chatlas/_chat.py,sha256=15j4w1cvFXrqsf7sGMv8B_TidEiow-szbRr6G-HiSqM,45990
4
- chatlas/_content.py,sha256=79FTmKrByS-rlg6XTbKQh4klaA4O3MJiljMrYrkr1mU,6334
5
- chatlas/_content_image.py,sha256=4nk9wTvLtNmtcytdFp8p9otEV5-0_K6wzIxCyK0PIEI,8367
6
- chatlas/_display.py,sha256=_IcQcvpyTNjGHOpY70_LOrDWwTjzdkziy6pTvxHEiWI,4053
7
- chatlas/_github.py,sha256=D3L7Qu35K-M1qEW7-w-Oq-pF-9mVetia3MHYNNLEYtU,4373
8
- chatlas/_google.py,sha256=RUXKUChiEqLzhIO3aWQn2dooJEmHNaX8gFK1a5Pzw1Q,18763
9
- chatlas/_groq.py,sha256=3VnYiKdxJTHPhEgUKnL2nY5uYL2L4PKBo7GZMwR0D8k,4158
10
- chatlas/_interpolate.py,sha256=ykwLP3x-ya9Q33U4knSU75dtk6pzJAeythEEIW-43Pc,3631
11
- chatlas/_logging.py,sha256=7a20sAl1PkW1qBNrfd_ieUbQXV8Gf4Vuf0Wn62LNBmk,2290
12
- chatlas/_merge.py,sha256=SGj_BetgA7gaOqSBKOhYmW3CYeQKTEehFrXvx3y4OYE,3924
13
- chatlas/_ollama.py,sha256=ze-RoHEbf62dYmXDDKjNGqaEZaKCZdcBEyFwQMDQxkQ,3760
14
- chatlas/_openai.py,sha256=Z1y_NdWr3TyrrFGtvYEaTU7cdMQAqLzMiK67-kIa8vM,23852
15
- chatlas/_perplexity.py,sha256=Bw_mlM8N8egGKIrbNerTn2pMlybugADOshjYOfN1ixM,4446
16
- chatlas/_provider.py,sha256=YmdBbz_u5aP_kBxl6s26OPiSnWG_vZ_fvf9L2qvBmyI,3809
17
- chatlas/_tokens.py,sha256=3W3EPUp9eWXUiwuzJwEPBv43AUznbK46pm59Htti7z4,2392
18
- chatlas/_tokens_old.py,sha256=L9d9oafrXvEx2u4nIn_Jjn7adnQyLBnYBuPwJUE8Pl8,5005
19
- chatlas/_tools.py,sha256=-qt4U1AFkebQoX9kpsBy5QXK8a2PpHX6Amgm44gcQ68,4113
20
- chatlas/_turn.py,sha256=nKwk20FrOIrZX4xJxdGyUEpwUH2H-UYcoJLlO2ZD5iU,4836
21
- chatlas/_typing_extensions.py,sha256=YdzmlyPSBpIEcsOkoz12e6jETT1XEMV2Q72haE4cfwY,1036
22
- chatlas/_utils.py,sha256=qAiWuDx-uG8BGFZ_PWvum9wpN-WogdItO32X4pRhhLs,2762
23
- chatlas/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
- chatlas/types/__init__.py,sha256=P_EDL4eqsigKwB-u2qRmKlYQS5Y65m7oWjGC3cYmxO4,719
25
- chatlas/types/anthropic/__init__.py,sha256=OwubA-DPHYpYo0XyRyAFwftOI0mOxtHzAyhUSLcDx54,417
26
- chatlas/types/anthropic/_client.py,sha256=G0LRhoFBcsSOMr5qhP-0rAScsVXaVlHCpggfVp54bnQ,690
27
- chatlas/types/anthropic/_client_bedrock.py,sha256=mNazQlu0pQt8JdzrYn3LKNgE4n732GjhQUJdQQK9QkY,785
28
- chatlas/types/anthropic/_submit.py,sha256=cSSomSJ2zfAHu8ilzMN3XxRTLANh00oK_t5kH6N2lCI,2091
29
- chatlas/types/google/__init__.py,sha256=ZJhi8Kwvio2zp8T1TQqmvdHqkS-Khb6BGESPjREADgo,337
30
- chatlas/types/google/_client.py,sha256=t7aKbxYq_xOA1Z3RnWcjewifdQFSHi7vKEj6MyKMCJk,729
31
- chatlas/types/google/_submit.py,sha256=b-ZqMvI551Ia7pFlWdqUQJjov3neHmVwLFw-P2bgU8w,1883
32
- chatlas/types/openai/__init__.py,sha256=Q2RAr1bSH1nHsxICK05nAmKmxdhKmhbBkWD_XHiVSrI,411
33
- chatlas/types/openai/_client.py,sha256=YGm_EHtRSSHeeOZe-CV7oNvMJpEblEta3UTuU7lSRO8,754
34
- chatlas/types/openai/_client_azure.py,sha256=jx8D_p46CLDGzTP-k-TtGzj-f3junj6or-86m8DD_0w,858
35
- chatlas/types/openai/_submit.py,sha256=VX2D1eQHF19XRIt-oq90cBSw_3ChokGpwVG7-b2AJOU,6091
36
- chatlas-0.4.0.dist-info/METADATA,sha256=-ayykYk3sVvvPRlW6jj15U6S76ywblY0p1sD8trc640,13389
37
- chatlas-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
- chatlas-0.4.0.dist-info/RECORD,,