not-again-ai 0.3.1__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/LICENSE +1 -1
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/PKG-INFO +25 -21
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/README.md +16 -11
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/pyproject.toml +10 -26
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/llm/__init__.py +1 -1
- not_again_ai-0.4.1/src/not_again_ai/llm/chat_completion.py +102 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/llm/context_management.py +2 -2
- not_again_ai-0.4.1/src/not_again_ai/llm/embeddings.py +62 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/llm/openai_client.py +6 -4
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/llm/prompts.py +9 -8
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/llm/tokens.py +15 -10
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/viz/time_series.py +5 -3
- not_again_ai-0.3.1/src/not_again_ai/llm/chat_completion.py +0 -77
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/__init__.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/base/__init__.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/base/file_system.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/base/parallel.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/py.typed +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/statistics/__init__.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/statistics/dependence.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/viz/__init__.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/viz/barplots.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/viz/distributions.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/viz/scatterplot.py +0 -0
- {not_again_ai-0.3.1 → not_again_ai-0.4.1}/src/not_again_ai/viz/utils.py +0 -0
@@ -1,12 +1,12 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: not-again-ai
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.1
|
4
4
|
Summary: Designed to once and for all collect all the little things that come up over and over again in AI projects and put them in one place.
|
5
5
|
Home-page: https://github.com/DaveCoDev/not-again-ai
|
6
6
|
License: MIT
|
7
7
|
Author: DaveCoDev
|
8
8
|
Author-email: dave.co.dev@gmail.com
|
9
|
-
Requires-Python: >=3.
|
9
|
+
Requires-Python: >=3.11,<3.13
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
11
11
|
Classifier: Intended Audience :: Developers
|
12
12
|
Classifier: Intended Audience :: Science/Research
|
@@ -14,7 +14,6 @@ Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
15
15
|
Classifier: Programming Language :: Python
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
17
|
-
Classifier: Programming Language :: Python :: 3.10
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
19
18
|
Classifier: Programming Language :: Python :: 3.12
|
20
19
|
Classifier: Programming Language :: Python :: 3 :: Only
|
@@ -22,13 +21,13 @@ Classifier: Typing :: Typed
|
|
22
21
|
Provides-Extra: llm
|
23
22
|
Provides-Extra: statistics
|
24
23
|
Provides-Extra: viz
|
25
|
-
Requires-Dist:
|
26
|
-
Requires-Dist:
|
27
|
-
Requires-Dist:
|
28
|
-
Requires-Dist:
|
29
|
-
Requires-Dist: scikit-learn (>=1.
|
30
|
-
Requires-Dist: scipy (>=1.
|
31
|
-
Requires-Dist: seaborn (>=0.13.
|
24
|
+
Requires-Dist: numpy (>=1.26.3,<2.0.0) ; extra == "statistics" or extra == "viz"
|
25
|
+
Requires-Dist: openai (>=1.10.0,<2.0.0) ; extra == "llm"
|
26
|
+
Requires-Dist: pandas (>=2.2.0,<3.0.0) ; extra == "viz"
|
27
|
+
Requires-Dist: python-liquid (>=1.10.2,<2.0.0) ; extra == "llm"
|
28
|
+
Requires-Dist: scikit-learn (>=1.4.0,<2.0.0) ; extra == "statistics"
|
29
|
+
Requires-Dist: scipy (>=1.12.0,<2.0.0) ; extra == "statistics"
|
30
|
+
Requires-Dist: seaborn (>=0.13.2,<0.14.0) ; extra == "viz"
|
32
31
|
Requires-Dist: tiktoken (>=0.5.2,<0.6.0) ; extra == "llm"
|
33
32
|
Project-URL: Documentation, https://github.com/DaveCoDev/not-again-ai
|
34
33
|
Project-URL: Repository, https://github.com/DaveCoDev/not-again-ai
|
@@ -39,7 +38,6 @@ Description-Content-Type: text/markdown
|
|
39
38
|
[![GitHub Actions][github-actions-badge]](https://github.com/johnthagen/python-blueprint/actions)
|
40
39
|
[![Packaged with Poetry][poetry-badge]](https://python-poetry.org/)
|
41
40
|
[![Nox][nox-badge]](https://github.com/wntrblm/nox)
|
42
|
-
[![Code style: Black][black-badge]](https://github.com/psf/black)
|
43
41
|
[![Ruff][ruff-badge]](https://github.com/astral-sh/ruff)
|
44
42
|
[![Type checked with mypy][mypy-badge]](https://mypy-lang.org/)
|
45
43
|
|
@@ -56,7 +54,7 @@ Description-Content-Type: text/markdown
|
|
56
54
|
|
57
55
|
# Installation
|
58
56
|
|
59
|
-
Requires: Python 3.
|
57
|
+
Requires: Python 3.11, or 3.12
|
60
58
|
|
61
59
|
Install the entire package from [PyPI](https://pypi.org/project/not-again-ai/) with:
|
62
60
|
|
@@ -81,6 +79,15 @@ The base package includes only functions that have minimal external dependencies
|
|
81
79
|
## LLM (Large Language Model)
|
82
80
|
[README](https://github.com/DaveCoDev/not-again-ai/blob/main/readmes/llm.md)
|
83
81
|
|
82
|
+
Supports OpenAI chat completions and text embeddings. Includes functions for creating chat completion prompts, token management, and context management.
|
83
|
+
|
84
|
+
One example:
|
85
|
+
```python
|
86
|
+
client = openai_client()
|
87
|
+
messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}]
|
88
|
+
response = chat_completion(messages=messages, model="gpt-3.5-turbo", max_tokens=100, client=client)["message"]
|
89
|
+
>>> "Hello! How can I help you today?"
|
90
|
+
```
|
84
91
|
|
85
92
|
## Statistics
|
86
93
|
[README](https://github.com/DaveCoDev/not-again-ai/blob/main/readmes/statistics.md)
|
@@ -266,9 +273,11 @@ To pass arguments to `pytest` through `nox`:
|
|
266
273
|
|
267
274
|
## Code Style Checking
|
268
275
|
|
269
|
-
[PEP 8](https://peps.python.org/pep-0008/) is the universally accepted style guide for
|
270
|
-
|
271
|
-
|
276
|
+
[PEP 8](https://peps.python.org/pep-0008/) is the universally accepted style guide for Python
|
277
|
+
code. PEP 8 code compliance is verified using [Ruff][Ruff]. Ruff is configured in the
|
278
|
+
`[tool.ruff]` section of [`pyproject.toml`](./pyproject.toml).
|
279
|
+
|
280
|
+
[Ruff]: https://github.com/astral-sh/ruff
|
272
281
|
|
273
282
|
To lint code, run:
|
274
283
|
|
@@ -284,12 +293,7 @@ To automatically fix fixable lint errors, run:
|
|
284
293
|
|
285
294
|
## Automated Code Formatting
|
286
295
|
|
287
|
-
|
288
|
-
automatically sorted and grouped using [Ruff](https://github.com/astral-sh/ruff).
|
289
|
-
|
290
|
-
These tools are configured by:
|
291
|
-
|
292
|
-
- [`pyproject.toml`](./pyproject.toml)
|
296
|
+
[Ruff][Ruff] is used to automatically format code and group and sort imports.
|
293
297
|
|
294
298
|
To automatically format code, run:
|
295
299
|
|
@@ -3,7 +3,6 @@
|
|
3
3
|
[![GitHub Actions][github-actions-badge]](https://github.com/johnthagen/python-blueprint/actions)
|
4
4
|
[![Packaged with Poetry][poetry-badge]](https://python-poetry.org/)
|
5
5
|
[![Nox][nox-badge]](https://github.com/wntrblm/nox)
|
6
|
-
[![Code style: Black][black-badge]](https://github.com/psf/black)
|
7
6
|
[![Ruff][ruff-badge]](https://github.com/astral-sh/ruff)
|
8
7
|
[![Type checked with mypy][mypy-badge]](https://mypy-lang.org/)
|
9
8
|
|
@@ -20,7 +19,7 @@
|
|
20
19
|
|
21
20
|
# Installation
|
22
21
|
|
23
|
-
Requires: Python 3.
|
22
|
+
Requires: Python 3.11, or 3.12
|
24
23
|
|
25
24
|
Install the entire package from [PyPI](https://pypi.org/project/not-again-ai/) with:
|
26
25
|
|
@@ -45,6 +44,15 @@ The base package includes only functions that have minimal external dependencies
|
|
45
44
|
## LLM (Large Language Model)
|
46
45
|
[README](https://github.com/DaveCoDev/not-again-ai/blob/main/readmes/llm.md)
|
47
46
|
|
47
|
+
Supports OpenAI chat completions and text embeddings. Includes functions for creating chat completion prompts, token management, and context management.
|
48
|
+
|
49
|
+
One example:
|
50
|
+
```python
|
51
|
+
client = openai_client()
|
52
|
+
messages = [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Hello!"}]
|
53
|
+
response = chat_completion(messages=messages, model="gpt-3.5-turbo", max_tokens=100, client=client)["message"]
|
54
|
+
>>> "Hello! How can I help you today?"
|
55
|
+
```
|
48
56
|
|
49
57
|
## Statistics
|
50
58
|
[README](https://github.com/DaveCoDev/not-again-ai/blob/main/readmes/statistics.md)
|
@@ -230,9 +238,11 @@ To pass arguments to `pytest` through `nox`:
|
|
230
238
|
|
231
239
|
## Code Style Checking
|
232
240
|
|
233
|
-
[PEP 8](https://peps.python.org/pep-0008/) is the universally accepted style guide for
|
234
|
-
|
235
|
-
|
241
|
+
[PEP 8](https://peps.python.org/pep-0008/) is the universally accepted style guide for Python
|
242
|
+
code. PEP 8 code compliance is verified using [Ruff][Ruff]. Ruff is configured in the
|
243
|
+
`[tool.ruff]` section of [`pyproject.toml`](./pyproject.toml).
|
244
|
+
|
245
|
+
[Ruff]: https://github.com/astral-sh/ruff
|
236
246
|
|
237
247
|
To lint code, run:
|
238
248
|
|
@@ -248,12 +258,7 @@ To automatically fix fixable lint errors, run:
|
|
248
258
|
|
249
259
|
## Automated Code Formatting
|
250
260
|
|
251
|
-
|
252
|
-
automatically sorted and grouped using [Ruff](https://github.com/astral-sh/ruff).
|
253
|
-
|
254
|
-
These tools are configured by:
|
255
|
-
|
256
|
-
- [`pyproject.toml`](./pyproject.toml)
|
261
|
+
[Ruff][Ruff] is used to automatically format code and group and sort imports.
|
257
262
|
|
258
263
|
To automatically format code, run:
|
259
264
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "not-again-ai"
|
3
|
-
version = "0.
|
3
|
+
version = "0.4.1"
|
4
4
|
description = "Designed to once and for all collect all the little things that come up over and over again in AI projects and put them in one place."
|
5
5
|
authors = ["DaveCoDev <dave.co.dev@gmail.com>"]
|
6
6
|
license = "MIT"
|
@@ -16,7 +16,6 @@ classifiers = [
|
|
16
16
|
"Programming Language :: Python",
|
17
17
|
"Programming Language :: Python :: 3",
|
18
18
|
"Programming Language :: Python :: 3 :: Only",
|
19
|
-
"Programming Language :: Python :: 3.10",
|
20
19
|
"Programming Language :: Python :: 3.11",
|
21
20
|
"Programming Language :: Python :: 3.12",
|
22
21
|
"Typing :: Typed",
|
@@ -26,28 +25,25 @@ classifiers = [
|
|
26
25
|
# Some packages, such as scipy, constrain their upper bound of Python versions they support.
|
27
26
|
# Without also constraining the upper bound here, Poetry will not select those versions and will
|
28
27
|
# result in an old version being resolved/locked.
|
29
|
-
python = "^3.
|
28
|
+
python = "^3.11, <3.13"
|
30
29
|
|
31
30
|
# Optional dependencies are defined here, and groupings are defined below.
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
scipy = { version = "^1.
|
37
|
-
scikit-learn = { version = "^1.
|
38
|
-
seaborn = { version = "^0.13.
|
31
|
+
numpy = { version = "^1.26.3", optional = true }
|
32
|
+
openai = { version = "^1.10.0", optional = true }
|
33
|
+
pandas = { version = "^2.2.0", optional = true }
|
34
|
+
python-liquid = { version = "^1.10.2", optional = true }
|
35
|
+
scipy = { version = "^1.12.0", optional = true }
|
36
|
+
scikit-learn = { version = "^1.4.0", optional = true }
|
37
|
+
seaborn = { version = "^0.13.2", optional = true }
|
39
38
|
tiktoken = { version = "^0.5.2", optional = true }
|
40
39
|
|
41
40
|
[tool.poetry.extras]
|
42
|
-
llm = ["
|
41
|
+
llm = ["openai", "python-liquid", "tiktoken"]
|
43
42
|
statistics = ["numpy", "scikit-learn", "scipy"]
|
44
43
|
viz = ["numpy", "pandas", "seaborn"]
|
45
44
|
|
46
45
|
[tool.poetry.group.nox.dependencies]
|
47
46
|
nox-poetry = "*"
|
48
|
-
# TODO: Remove this after virtualenv supports platformdirs 4.
|
49
|
-
# https://github.com/pypa/virtualenv/issues/2666
|
50
|
-
platformdirs = "<4"
|
51
47
|
|
52
48
|
[tool.poetry.group.test.dependencies]
|
53
49
|
pytest = "*"
|
@@ -65,9 +61,6 @@ mypy = "*"
|
|
65
61
|
[tool.poetry.group.lint.dependencies]
|
66
62
|
ruff = "*"
|
67
63
|
|
68
|
-
[tool.poetry.group.fmt.dependencies]
|
69
|
-
black = "*"
|
70
|
-
|
71
64
|
[tool.poetry.group.docs.dependencies]
|
72
65
|
mkdocs-material = "*"
|
73
66
|
mkdocs-htmlproofer-plugin = "*"
|
@@ -111,8 +104,6 @@ unfixable = ["F401"]
|
|
111
104
|
[tool.ruff.isort]
|
112
105
|
force-sort-within-sections = true
|
113
106
|
split-on-trailing-comma = false
|
114
|
-
# For non-src directory projects, explicitly set top level package names:
|
115
|
-
# known-first-party = ["my-app"]
|
116
107
|
|
117
108
|
[tool.ruff.flake8-tidy-imports]
|
118
109
|
ban-relative-imports = "all"
|
@@ -120,13 +111,6 @@ ban-relative-imports = "all"
|
|
120
111
|
[tool.ruff.flake8-bugbear]
|
121
112
|
extend-immutable-calls = ["typer.Argument"]
|
122
113
|
|
123
|
-
[tool.black]
|
124
|
-
line-length = 120
|
125
|
-
target-version = ["py39", "py310", "py311", "py312"]
|
126
|
-
# black will automatically exclude all files listed in .gitignore
|
127
|
-
# If you need to exclude additional folders, consider using extend-exclude to avoid disabling the
|
128
|
-
# default .gitignore behaviour.
|
129
|
-
|
130
114
|
[tool.pytest.ini_options]
|
131
115
|
addopts = [
|
132
116
|
"--strict-config",
|
@@ -0,0 +1,102 @@
|
|
1
|
+
import contextlib
|
2
|
+
import json
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
from openai import OpenAI
|
6
|
+
|
7
|
+
|
8
|
+
def chat_completion(
|
9
|
+
messages: list[dict[str, str]],
|
10
|
+
model: str,
|
11
|
+
client: OpenAI,
|
12
|
+
tools: list[dict[str, Any]] | None = None,
|
13
|
+
tool_choice: str = "auto",
|
14
|
+
max_tokens: int | None = None,
|
15
|
+
temperature: float = 0.7,
|
16
|
+
json_mode: bool = False,
|
17
|
+
**kwargs: Any,
|
18
|
+
) -> dict[str, Any]:
|
19
|
+
"""Get an OpenAI chat completion response: https://platform.openai.com/docs/api-reference/chat/create
|
20
|
+
|
21
|
+
Args:
|
22
|
+
messages (list): A list of messages comprising the conversation so far.
|
23
|
+
model (str): ID of the model to use. See the model endpoint compatibility table:
|
24
|
+
https://platform.openai.com/docs/models/model-endpoint-compatibility
|
25
|
+
for details on which models work with the Chat API.
|
26
|
+
client (OpenAI): An instance of the OpenAI client.
|
27
|
+
tools (list[dict[str, Any]], optional): A list of tools the model may generate JSON inputs for.
|
28
|
+
Defaults to None.
|
29
|
+
tool_choice (str, optional): The tool choice to use. Can be "auto", "none", or a specific function name.
|
30
|
+
Defaults to "auto".
|
31
|
+
max_tokens (int, optional): The maximum number of tokens to generate in the chat completion.
|
32
|
+
Defaults to None, which automatically limits to the model's maximum context length.
|
33
|
+
temperature (float, optional): What sampling temperature to use, between 0 and 2.
|
34
|
+
Higher values like 0.8 will make the output more random,
|
35
|
+
while lower values like 0.2 will make it more focused and deterministic. Defaults to 0.7.
|
36
|
+
json_mode (bool, optional): When JSON mode is enabled, the model is constrained to only
|
37
|
+
generate strings that parse into valid JSON object and will return a dictionary.
|
38
|
+
See https://platform.openai.com/docs/guides/text-generation/json-mode
|
39
|
+
**kwargs: Additional keyword arguments to pass to the OpenAI client chat completion.
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
dict: A dictionary containing the following keys:
|
43
|
+
- "finish_reason" (str): The reason the model stopped generating further tokens.
|
44
|
+
Can be "stop", "length", or "tool_calls".
|
45
|
+
- "tool_names" (list[str], optional): The names of the tools called by the model.
|
46
|
+
- "tool_args_list" (list[dict], optional): The arguments of the tools called by the model.
|
47
|
+
- "message" (str | dict): The content of the generated assistant message.
|
48
|
+
If json_mode is True, this will be a dictionary.
|
49
|
+
- "completion_tokens" (int): The number of tokens used by the model to generate the completion.
|
50
|
+
- "prompt_tokens" (int): The number of tokens in the generated response.
|
51
|
+
"""
|
52
|
+
response_format = {"type": "json_object"} if json_mode else None
|
53
|
+
|
54
|
+
kwargs.update(
|
55
|
+
{
|
56
|
+
"messages": messages,
|
57
|
+
"model": model,
|
58
|
+
"tools": tools,
|
59
|
+
"max_tokens": max_tokens,
|
60
|
+
"temperature": temperature,
|
61
|
+
"response_format": response_format,
|
62
|
+
"n": 1,
|
63
|
+
}
|
64
|
+
)
|
65
|
+
|
66
|
+
if tools is not None:
|
67
|
+
if tool_choice not in ["none", "auto"]:
|
68
|
+
kwargs["tool_choice"] = {"type": "function", "function": {"name": tool_choice}}
|
69
|
+
else:
|
70
|
+
kwargs["tool_choice"] = tool_choice
|
71
|
+
|
72
|
+
# Call the function with the set parameters
|
73
|
+
response = client.chat.completions.create(**kwargs)
|
74
|
+
|
75
|
+
response_data = {}
|
76
|
+
finish_reason = response.choices[0].finish_reason
|
77
|
+
response_data["finish_reason"] = finish_reason
|
78
|
+
|
79
|
+
# Not checking finish_reason=="tool_calls" here because when a user providea function name as tool_choice,
|
80
|
+
# the finish reason is "stop", not "tool_calls"
|
81
|
+
tool_calls = response.choices[0].message.tool_calls
|
82
|
+
if tool_calls:
|
83
|
+
tool_names = []
|
84
|
+
tool_args_list = []
|
85
|
+
for tool_call in tool_calls:
|
86
|
+
tool_names.append(tool_call.function.name)
|
87
|
+
tool_args_list.append(json.loads(tool_call.function.arguments))
|
88
|
+
response_data["tool_names"] = tool_names
|
89
|
+
response_data["tool_args_list"] = tool_args_list
|
90
|
+
elif finish_reason == "stop" or finish_reason == "length":
|
91
|
+
message = response.choices[0].message.content
|
92
|
+
if json_mode:
|
93
|
+
with contextlib.suppress(json.JSONDecodeError):
|
94
|
+
message = json.loads(message)
|
95
|
+
response_data["message"] = message
|
96
|
+
|
97
|
+
usage = response.usage
|
98
|
+
if usage is not None:
|
99
|
+
response_data["completion_tokens"] = usage.completion_tokens
|
100
|
+
response_data["prompt_tokens"] = usage.prompt_tokens
|
101
|
+
|
102
|
+
return response_data
|
@@ -18,7 +18,7 @@ def priority_truncation(
|
|
18
18
|
variables: dict[str, str],
|
19
19
|
priority: list[str],
|
20
20
|
token_limit: int,
|
21
|
-
model: str = "gpt-3.5-turbo-
|
21
|
+
model: str = "gpt-3.5-turbo-1106",
|
22
22
|
) -> list[dict[str, str]]:
|
23
23
|
"""Formats messages_unformatted and injects variables into the messages in the order of priority, truncating the messages to fit the token limit.
|
24
24
|
|
@@ -37,7 +37,7 @@ def priority_truncation(
|
|
37
37
|
variables: A dictionary where each key-value pair represents a variable name and its value to inject.
|
38
38
|
priority: A list of variable names in their order of priority.
|
39
39
|
token_limit: The maximum number of tokens allowed in the messages.
|
40
|
-
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-
|
40
|
+
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-1106".
|
41
41
|
"""
|
42
42
|
|
43
43
|
# Check if all variables in the priority list are in the variables dict.
|
@@ -0,0 +1,62 @@
|
|
1
|
+
from typing import Any
|
2
|
+
|
3
|
+
from openai import OpenAI
|
4
|
+
|
5
|
+
|
6
|
+
def embed_text(
|
7
|
+
text: str | list[str],
|
8
|
+
client: OpenAI,
|
9
|
+
model: str = "text-embedding-3-large",
|
10
|
+
dimensions: int | None = None,
|
11
|
+
encoding_format: str = "float",
|
12
|
+
**kwargs: Any,
|
13
|
+
) -> list[float] | str | list[list[float]] | list[str]:
|
14
|
+
"""Generates an embedding vector for a given text using OpenAI's API.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
text (str | list[str]): The input text to be embedded. Each text should not exceed 8191 tokens, which is the max for V2 and V3 models
|
18
|
+
client (OpenAI): The OpenAI client used to interact with the API.
|
19
|
+
model (str, optional): The ID of the model to use for embedding.
|
20
|
+
Defaults to "text-embedding-3-large".
|
21
|
+
Choose from text-embedding-3-small, text-embedding-3-large, text-embedding-ada-002.
|
22
|
+
See https://platform.openai.com/docs/models/embeddings for more details.
|
23
|
+
dimensions (int | None, optional): The number of dimensions for the output embeddings.
|
24
|
+
This is only supported in "text-embedding-3" and later models. Defaults to None.
|
25
|
+
encoding_format (str, optional): The format for the returned embeddings. Can be either "float" or "base64".
|
26
|
+
Defaults to "float".
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
list[float] | str | list[list[float]] | list[str]: The embedding vector represented as a list of floats or base64 encoded string.
|
30
|
+
If multiple text inputs are provided, a list of embedding vectors is returned.
|
31
|
+
The length and format of the vector depend on the model, encoding_format, and dimensions.
|
32
|
+
|
33
|
+
Raises:
|
34
|
+
ValueError: If 'text-embedding-ada-002' model is used and dimensions are specified,
|
35
|
+
as this model does not support specifying dimensions.
|
36
|
+
|
37
|
+
Example:
|
38
|
+
client = OpenAI()
|
39
|
+
embedding = embed_text("Example text", client, model="text-embedding-ada-002")
|
40
|
+
"""
|
41
|
+
if model == "text-embedding-ada-002" and dimensions:
|
42
|
+
# text-embedding-ada-002 does not support dimensions
|
43
|
+
raise ValueError("text-embedding-ada-002 does not support dimensions")
|
44
|
+
|
45
|
+
kwargs = {
|
46
|
+
"model": model,
|
47
|
+
"input": text,
|
48
|
+
"encoding_format": encoding_format,
|
49
|
+
}
|
50
|
+
if dimensions:
|
51
|
+
kwargs["dimensions"] = dimensions
|
52
|
+
|
53
|
+
response = client.embeddings.create(**kwargs)
|
54
|
+
|
55
|
+
responses = []
|
56
|
+
for embedding in response.data:
|
57
|
+
responses.append(embedding.embedding)
|
58
|
+
|
59
|
+
if len(responses) == 1:
|
60
|
+
return responses[0]
|
61
|
+
|
62
|
+
return responses
|
@@ -21,10 +21,12 @@ def openai_client(
|
|
21
21
|
Defaults to 'openai'.
|
22
22
|
api_key (str, optional): The API key to authenticate the client. If not provided,
|
23
23
|
OpenAI automatically uses `OPENAI_API_KEY` from the environment.
|
24
|
-
organization (str, optional): The ID of the organization
|
24
|
+
organization (str, optional): The ID of the organization. If not provided,
|
25
25
|
OpenAI automotically uses `OPENAI_ORG_ID` from the environment.
|
26
|
-
timeout (float, optional):
|
27
|
-
max_retries (int, optional):
|
26
|
+
timeout (float, optional): By default requests time out after 10 minutes.
|
27
|
+
max_retries (int, optional): Certain errors are automatically retried 2 times by default,
|
28
|
+
with a short exponential backoff. Connection errors (for example, due to a network connectivity problem),
|
29
|
+
408 Request Timeout, 409 Conflict, 429 Rate Limit, and >=500 Internal errors are all retried by default.
|
28
30
|
|
29
31
|
Returns:
|
30
32
|
OpenAI: An instance of the OpenAI client.
|
@@ -34,7 +36,7 @@ def openai_client(
|
|
34
36
|
NotImplementedError: If the specified API type is recognized but not yet supported (e.g., 'azure_openai').
|
35
37
|
|
36
38
|
Examples:
|
37
|
-
>>> client =
|
39
|
+
>>> client = openai_client(api_type="openai", api_key="YOUR_API_KEY")
|
38
40
|
"""
|
39
41
|
if api_type not in ["openai", "azure_openai"]:
|
40
42
|
raise InvalidOAIAPITypeError(f"Invalid OAIAPIType: {api_type}. Must be 'openai' or 'azure_openai'.")
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import
|
1
|
+
from liquid import Template
|
2
2
|
|
3
3
|
|
4
4
|
def _validate_message(message: dict[str, str]) -> bool:
|
@@ -19,15 +19,13 @@ def _validate_message(message: dict[str, str]) -> bool:
|
|
19
19
|
|
20
20
|
|
21
21
|
def chat_prompt(messages_unformatted: list[dict[str, str]], variables: dict[str, str]) -> list[dict[str, str]]:
|
22
|
-
"""
|
23
|
-
|
24
|
-
The content of each message is treated as a Jinja2 template that is rendered
|
25
|
-
with the provided variables.
|
22
|
+
"""
|
23
|
+
Formats a list of messages for OpenAI's chat completion API using Liquid templating.
|
26
24
|
|
27
25
|
Args:
|
28
26
|
messages_unformatted: A list of dictionaries where each dictionary
|
29
27
|
represents a message. Each message must have 'role' and 'content'
|
30
|
-
keys with string values, where content is a
|
28
|
+
keys with string values, where content is a Liquid template.
|
31
29
|
variables: A dictionary where each key-value pair represents a variable
|
32
30
|
name and its value for template rendering.
|
33
31
|
|
@@ -47,10 +45,13 @@ def chat_prompt(messages_unformatted: list[dict[str, str]], variables: dict[str,
|
|
47
45
|
{"role": "user", "content": "Help me write Python code for the fibonnaci sequence"}
|
48
46
|
]
|
49
47
|
"""
|
48
|
+
|
50
49
|
messages_formatted = messages_unformatted.copy()
|
51
50
|
for message in messages_formatted:
|
52
|
-
# Validate each message and return a ValueError if any message is invalid
|
53
51
|
if not _validate_message(message):
|
54
52
|
raise ValueError(f"Invalid message: {message}")
|
55
|
-
|
53
|
+
|
54
|
+
liquid_template = Template(message["content"])
|
55
|
+
message["content"] = liquid_template.render(**variables)
|
56
|
+
|
56
57
|
return messages_formatted
|
@@ -1,15 +1,14 @@
|
|
1
1
|
import tiktoken
|
2
2
|
|
3
3
|
|
4
|
-
def truncate_str(text: str, max_len: int, model: str = "gpt-3.5-turbo-
|
4
|
+
def truncate_str(text: str, max_len: int, model: str = "gpt-3.5-turbo-1106") -> str:
|
5
5
|
"""Truncates a string to a maximum token length.
|
6
6
|
|
7
7
|
Args:
|
8
8
|
text: The string to truncate.
|
9
9
|
max_len: The maximum number of tokens to keep.
|
10
|
-
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-
|
11
|
-
See https://
|
12
|
-
for a list of OpenAI models.
|
10
|
+
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-1106".
|
11
|
+
See https://platform.openai.com/docs/models for a list of OpenAI models.
|
13
12
|
|
14
13
|
Returns:
|
15
14
|
The truncated string.
|
@@ -30,12 +29,13 @@ def truncate_str(text: str, max_len: int, model: str = "gpt-3.5-turbo-0613") ->
|
|
30
29
|
return text
|
31
30
|
|
32
31
|
|
33
|
-
def num_tokens_in_string(text: str, model: str = "gpt-3.5-turbo-
|
32
|
+
def num_tokens_in_string(text: str, model: str = "gpt-3.5-turbo-1106") -> int:
|
34
33
|
"""Return the number of tokens in a string.
|
35
34
|
|
36
35
|
Args:
|
37
36
|
text: The string to count the tokens.
|
38
|
-
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-
|
37
|
+
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-1106".
|
38
|
+
See https://platform.openai.com/docs/models for a list of OpenAI models.
|
39
39
|
|
40
40
|
Returns:
|
41
41
|
The number of tokens in the string.
|
@@ -48,17 +48,17 @@ def num_tokens_in_string(text: str, model: str = "gpt-3.5-turbo-0613") -> int:
|
|
48
48
|
return len(encoding.encode(text))
|
49
49
|
|
50
50
|
|
51
|
-
def num_tokens_from_messages(messages: list[dict[str, str]], model: str = "gpt-3.5-turbo-
|
51
|
+
def num_tokens_from_messages(messages: list[dict[str, str]], model: str = "gpt-3.5-turbo-1106") -> int:
|
52
52
|
"""Return the number of tokens used by a list of messages.
|
53
53
|
NOTE: Does not support counting tokens used by function calling.
|
54
54
|
Reference: # https://github.com/openai/openai-cookbook/blob/main/examples/How_to_format_inputs_to_ChatGPT_models.ipynb
|
55
|
+
and https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
|
55
56
|
|
56
57
|
Args:
|
57
58
|
messages: A list of messages to count the tokens
|
58
59
|
should ideally be the result after calling llm.prompts.chat_prompt.
|
59
|
-
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-
|
60
|
-
See https://
|
61
|
-
for a list of OpenAI models.
|
60
|
+
model: The model to use for tokenization. Defaults to "gpt-3.5-turbo-1106".
|
61
|
+
See https://platform.openai.com/docs/models for a list of OpenAI models.
|
62
62
|
|
63
63
|
Returns:
|
64
64
|
The number of tokens used by the messages.
|
@@ -71,16 +71,21 @@ def num_tokens_from_messages(messages: list[dict[str, str]], model: str = "gpt-3
|
|
71
71
|
if model in {
|
72
72
|
"gpt-3.5-turbo-0613",
|
73
73
|
"gpt-3.5-turbo-16k-0613",
|
74
|
+
"gpt-3.5-turbo-1106",
|
74
75
|
"gpt-4-0314",
|
75
76
|
"gpt-4-32k-0314",
|
76
77
|
"gpt-4-0613",
|
77
78
|
"gpt-4-32k-0613",
|
79
|
+
"gpt-4-1106-preview",
|
80
|
+
"gpt-4-turbo-preview",
|
81
|
+
"gpt-4-0125-preview",
|
78
82
|
}:
|
79
83
|
tokens_per_message = 3 # every message follows <|start|>{role/name}\n{content}<|end|>\n
|
80
84
|
tokens_per_name = 1 # if there's a name, the role is omitted
|
81
85
|
elif model == "gpt-3.5-turbo-0301":
|
82
86
|
tokens_per_message = 4
|
83
87
|
tokens_per_name = -1
|
88
|
+
# Approximate catch-all. Assumes future versions of 3.5 and 4 will have the same token counts as the 0613 versions.
|
84
89
|
elif "gpt-3.5-turbo" in model:
|
85
90
|
return num_tokens_from_messages(messages, model="gpt-3.5-turbo-0613")
|
86
91
|
elif "gpt-4" in model:
|
@@ -13,9 +13,11 @@ from not_again_ai.viz.utils import reset_plot_libs
|
|
13
13
|
def ts_lineplot(
|
14
14
|
ts_data: list[float] | (npt.NDArray[np.float64] | npt.NDArray[np.int64]),
|
15
15
|
save_pathname: str,
|
16
|
-
ts_x:
|
17
|
-
|
18
|
-
|
16
|
+
ts_x: (
|
17
|
+
list[float]
|
18
|
+
| (npt.NDArray[np.float64] | (npt.NDArray[np.datetime64] | (npt.NDArray[np.int64] | pd.Series)))
|
19
|
+
| None
|
20
|
+
) = None,
|
19
21
|
ts_names: list[str] | None = None,
|
20
22
|
title: str | None = None,
|
21
23
|
xlabel: str | None = "Time",
|
@@ -1,77 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from openai import OpenAI
|
5
|
-
|
6
|
-
|
7
|
-
def chat_completion(
|
8
|
-
messages: list[dict[str, str]],
|
9
|
-
model: str,
|
10
|
-
client: OpenAI,
|
11
|
-
functions: list[dict[str, Any]] | None = None,
|
12
|
-
max_tokens: int | None = None,
|
13
|
-
temperature: float = 0.7,
|
14
|
-
**kwargs: Any,
|
15
|
-
) -> dict[str, Any]:
|
16
|
-
"""Get an OpenAI chat completion response: https://platform.openai.com/docs/api-reference/chat/create
|
17
|
-
|
18
|
-
Args:
|
19
|
-
messages (list): A list of messages comprising the conversation so far.
|
20
|
-
model (str): ID of the model to use. See the model endpoint compatibility table:
|
21
|
-
https://platform.openai.com/docs/models/model-endpoint-compatibility
|
22
|
-
for details on which models work with the Chat API.
|
23
|
-
client (OpenAI): An instance of the OpenAI client.
|
24
|
-
functions (list, optional): A list of functions the model may generate JSON inputs for. Defaults to None.
|
25
|
-
max_tokens (int, optional): The maximum number of tokens to generate in the chat completion.
|
26
|
-
Defaults to limited to the model's context length.
|
27
|
-
temperature (float, optional): What sampling temperature to use, between 0 and 2.
|
28
|
-
Higher values like 0.8 will make the output more random,
|
29
|
-
while lower values like 0.2 will make it more focused and deterministic. Defaults to 0.7.
|
30
|
-
**kwargs: Additional keyword arguments to pass to the OpenAI client chat completion.
|
31
|
-
|
32
|
-
Returns:
|
33
|
-
dict: A dictionary containing the following keys:
|
34
|
-
- "finish_reason" (str): The reason the model stopped generating further tokens. Can be "stop" or "function_call".
|
35
|
-
- "function_name" (str, optional): The name of the function called by the model, present only if "finish_reason" is "function_call".
|
36
|
-
- "function_args" (dict, optional): The arguments of the function called by the model, present only if "finish_reason" is "function_call".
|
37
|
-
- "message" (str, optional): The content of the generated assistant message, present only if "finish_reason" is "stop".
|
38
|
-
- "completion_tokens" (int): The number of tokens used by the model to generate the completion.
|
39
|
-
- "prompt_tokens" (int): The number of tokens in the generated response.
|
40
|
-
"""
|
41
|
-
if functions is None:
|
42
|
-
response = client.chat.completions.create(
|
43
|
-
messages=messages, # type: ignore
|
44
|
-
model=model,
|
45
|
-
max_tokens=max_tokens,
|
46
|
-
temperature=temperature,
|
47
|
-
n=1,
|
48
|
-
**kwargs,
|
49
|
-
)
|
50
|
-
else:
|
51
|
-
response = client.chat.completions.create( # type: ignore
|
52
|
-
messages=messages,
|
53
|
-
model=model,
|
54
|
-
functions=functions,
|
55
|
-
function_call="auto",
|
56
|
-
max_tokens=max_tokens,
|
57
|
-
temperature=temperature,
|
58
|
-
n=1,
|
59
|
-
**kwargs,
|
60
|
-
)
|
61
|
-
|
62
|
-
response_data = {}
|
63
|
-
finish_reason = response.choices[0].finish_reason
|
64
|
-
response_data["finish_reason"] = finish_reason
|
65
|
-
if finish_reason == "function_call":
|
66
|
-
function_call = response.choices[0].message.function_call
|
67
|
-
if function_call is not None:
|
68
|
-
response_data["function_name"] = function_call.name # type: ignore
|
69
|
-
response_data["function_args"] = json.loads(function_call.arguments)
|
70
|
-
elif finish_reason == "stop" or finish_reason == "length":
|
71
|
-
message = response.choices[0].message
|
72
|
-
response_data["message"] = message.content # type: ignore
|
73
|
-
usage = response.usage
|
74
|
-
if usage is not None:
|
75
|
-
response_data["completion_tokens"] = usage.completion_tokens # type: ignore
|
76
|
-
response_data["prompt_tokens"] = usage.prompt_tokens # type: ignore
|
77
|
-
return response_data
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|