Skip to content

Commit 5c958a0

Browse files
committed
[0.4.12] [fix] Threads API
1 parent 1216e8d commit 5c958a0

File tree

10 files changed

+87
-70
lines changed

10 files changed

+87
-70
lines changed

docs/changelog.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
tuneapi Changelog
2+
=================
3+
4+
0.4.12
5+
------
6+
7+
- Fix bug in Threads API where incorrect structure was sent by client
8+
- Add images support for Anthropic API
9+
- Add ``Message.images`` field to store all images

docs/conf.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77

88
sys.path.insert(0, os.path.abspath("../"))
99

10+
from tuneapi import __version__
11+
1012
# -- Project information -----------------------------------------------------
1113
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
1214

1315
project = "tuneapi"
1416
copyright = "2024, Frello Technologies"
1517
author = "Frello Technologies"
16-
release = "0.4.11"
18+
release = __version__
1719

1820
# -- General configuration ---------------------------------------------------
1921
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration

docs/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ TuneAPI
1515
source/tuneapi.apis
1616
source/tuneapi.types
1717
source/tuneapi.utils
18+
changelog
1819

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "tuneapi"
3-
version = "0.4.11"
3+
version = "0.4.12"
44
description = "Tune AI APIs."
55
authors = ["Frello Technology Private Limited <[email protected]>"]
66
license = "MIT"
@@ -17,6 +17,7 @@ cryptography = ">=42.0.5"
1717
tqdm = "^4.66.1"
1818
snowflake_id = "1.0.2"
1919
nutree = "0.8.0"
20+
pillow = "^10.2.0"
2021
boto3 = { version = "1.29.6", optional = true }
2122

2223
[tool.poetry.extras]

tuneapi/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Copyright © 2023- Frello Technology Private Limited
22

3-
__version__ = "0.4.11"
3+
__version__ = "0.4.12"

tuneapi/apis/model_anthropic.py

Lines changed: 53 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Copyright © 2024- Frello Technology Private Limited
22

3+
import re
34
import json
45
import requests
56
from typing import Optional, Dict, Any, Tuple, List
@@ -71,60 +72,55 @@ def _process_input(self, chats, token: Optional[str] = None):
7172
prev_tool_id = tu.get_random_string(5)
7273
for m in thread.chats[int(system != "") :]:
7374
if m.role == Message.HUMAN:
74-
claude_messages.append(
75-
{
76-
"role": "user",
77-
"content": [
75+
msg = {
76+
"role": "user",
77+
"content": [{"type": "text", "text": m.value.strip()}],
78+
}
79+
if m.images:
80+
for i in m.images:
81+
msg["content"].append(
7882
{
79-
"type": "text",
80-
"text": m.value.strip(),
83+
"type": "image",
84+
"source": {
85+
"type": "base64",
86+
"media_type": "image/png",
87+
"data": i,
88+
},
8189
}
82-
],
83-
}
84-
)
90+
)
8591
elif m.role == Message.GPT:
86-
claude_messages.append(
87-
{
88-
"role": "assistant",
89-
"content": [
90-
{
91-
"type": "text",
92-
"text": m.value.strip(),
93-
}
94-
],
95-
}
96-
)
92+
msg = {
93+
"role": "assistant",
94+
"content": [{"type": "text", "text": m.value.strip()}],
95+
}
9796
elif m.role == Message.FUNCTION_CALL:
9897
_m = tu.from_json(m.value) if isinstance(m.value, str) else m.value
99-
claude_messages.append(
100-
{
101-
"role": "assistant",
102-
"content": [
103-
{
104-
"type": "tool_use",
105-
"id": prev_tool_id,
106-
"name": _m["name"],
107-
"input": _m["arguments"],
108-
}
109-
],
110-
}
111-
)
98+
msg = {
99+
"role": "assistant",
100+
"content": [
101+
{
102+
"type": "tool_use",
103+
"id": prev_tool_id,
104+
"name": _m["name"],
105+
"input": _m["arguments"],
106+
}
107+
],
108+
}
112109
elif m.role == Message.FUNCTION_RESP:
113110
_m = tu.from_json(m.value) if isinstance(m.value, str) else m.value
114-
claude_messages.append(
115-
{
116-
"role": "user",
117-
"content": [
118-
{
119-
"type": "tool_result",
120-
"tool_use_id": prev_tool_id,
121-
"content": tu.to_json(_m, tight=True),
122-
}
123-
],
124-
}
125-
)
111+
msg = {
112+
"role": "user",
113+
"content": [
114+
{
115+
"type": "tool_result",
116+
"tool_use_id": prev_tool_id,
117+
"content": tu.to_json(_m, tight=True),
118+
}
119+
],
120+
}
126121
else:
127122
raise Exception(f"Unknown role: {m.role}")
123+
claude_messages.append(msg)
128124

129125
headers = {
130126
"x-api-key": token,
@@ -255,3 +251,15 @@ def stream_chat(
255251
except:
256252
break
257253
return
254+
255+
256+
# helper methods
257+
258+
259+
def get_section(tag: str, out: str) -> Optional[str]:
260+
pattern = re.compile("<" + tag + ">(.*?)</" + tag + ">", re.DOTALL)
261+
match = pattern.search(out)
262+
if match:
263+
content = match.group(1)
264+
return content
265+
return None

tuneapi/apis/threads.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ def get_sub(
1818
sess.headers.update({"x-tune-key": tune_api_key})
1919
if tune_org_id:
2020
sess.headers.update({"X-Organization-Id": tune_org_id})
21-
print("::::::", sess.headers)
2221
return tu.Subway(base_url, sess)
2322

2423

@@ -65,6 +64,7 @@ def put_thread(
6564
body["metadata"] = {"meta": tu.to_json(m, tight=True)}
6665

6766
tu.logger.info("Creating new thread")
67+
print(tu.to_json(body))
6868
out = self.sub.threads("post", json=body)
6969
thread.id = out["id"]
7070
return thread

tuneapi/types/chats.py

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import json
66
import copy
77
import random
8+
from PIL.Image import Image
89
from functools import partial
910
from collections.abc import Iterable
1011
from typing import Dict, List, Any, Tuple, Optional, Generator, Union
@@ -128,8 +129,8 @@ def __init__(
128129
self,
129130
value: str | float | List[Dict[str, Any]],
130131
role: str,
132+
images: List[str | Image] = [],
131133
id: str = None,
132-
fn_pairs: Optional[Tuple["Message", "Message"]] = None,
133134
**kwargs,
134135
):
135136
if role not in self.KNOWN_ROLES:
@@ -141,7 +142,12 @@ def __init__(
141142
self.value = value
142143
self.id = id or "msg_" + str(tu.get_snowflake())
143144
self.metadata = kwargs
144-
self.fn_pairs = fn_pairs
145+
self.images = images
146+
for i, img in enumerate(self.images):
147+
if isinstance(img, Image):
148+
buf = io.BytesIO()
149+
img.save(buf, "png")
150+
self.images[i] = tu.to_b64(buf.getvalue())
145151

146152
# validations
147153
if self.role == self.FUNCTION_CALL:
@@ -164,14 +170,7 @@ def __add__(self, other: str):
164170
return Message(self.value + other, self.role)
165171

166172
def __repr__(self) -> str:
167-
out = ""
168-
if self.fn_pairs:
169-
for fc, fr in self.fn_pairs:
170-
out += f"[[FC] {fc} => [FR] {fr}]"
171-
if out:
172-
out += " " + str(self.value)
173-
else:
174-
out = str(self.value)
173+
out = str(self.value)
175174
return out
176175

177176
def __getitem__(self, x):
@@ -190,13 +189,11 @@ def to_dict(
190189
meta: bool = False,
191190
):
192191
"""
193-
if format == ``ft`` then export to following format: ``{"from": "system/human/gpt", "value": "..."}``
194-
195-
elif format == ``api`` then ``{"role": "system/user/assistant", "content": [{"type": "text", "text": {"value": "..."}]}``
196-
197-
elif format == ``full`` then ``{"id": 1234421123, "role": "system/user/assistant", "content": [{"type": "text", "text": {"value": "..."}]}``
198-
199-
else export to following format: ``{"role": "system/user/assistant", "content": "..."}``
192+
Serialise the Message into a dictionary of different formats:
193+
- format == ``ft`` then export to following format: ``{"from": "system/human/gpt", "value": "..."}``
194+
- format == ``api`` then ``{"role": "system/user/assistant", "content": [{"type": "text", "text": {"value": "..."}]}``. This is used with TuneAPI
195+
- format == ``full`` then ``{"id": 1234421123, "role": "system/user/assistant", "content": [{"type": "text", "text": {"value": "..."}]}``
196+
- default: ``{"role": "system/user/assistant", "content": "..."}``
200197
"""
201198
role = self.role
202199

@@ -219,7 +216,7 @@ def to_dict(
219216
if ft:
220217
chat_message["value"] = self.value
221218
elif api:
222-
chat_message["content"] = [{"type": "text", "text": {"value": self.value}}]
219+
chat_message["content"] = [{"type": "text", "text": self.value}]
223220
else:
224221
chat_message["content"] = self.value
225222

tuneapi/utils/fs.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ def get_files_in_folder(
4040

4141
for f in files:
4242
for e in ext:
43-
print(e)
4443
if f.endswith(e):
4544
_fp = joinp(root, f)
4645
_ig_pat = ignore_pat.search(_fp)

tuneapi/utils/subway.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from tuneapi.utils.logger import logger
99
from tuneapi.utils.misc import SimplerTimes
10+
from tuneapi.utils.serdeser import to_json
1011

1112

1213
class SubwayClientError(Exception):
@@ -85,7 +86,7 @@ def u(self, attr: str) -> "Subway":
8586
def _renew_session(self):
8687
"""Renew the session"""
8788
_session = Session()
88-
if "token" in self._session.headers:
89+
if self._session.headers:
8990
_session.headers.update(**self._session.headers)
9091
self._session = _session
9192

@@ -152,7 +153,6 @@ def __call__(
152153

153154
if SimplerTimes.get_now_i64() - self._last_update > 120:
154155
self._renew_session()
155-
print(f"{url=}, {items=}")
156156
r = fn(url, **items, **kwargs)
157157
if 399 < r.status_code < 500:
158158
raise SubwayClientError(r.content.decode(), code=r.status_code)

0 commit comments

Comments
 (0)