llmir 0.0.1__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.
- llmir/__init__.py +17 -0
- llmir/adapter/__init__.py +10 -0
- llmir/adapter/openai.py +89 -0
- llmir/chunks.py +26 -0
- llmir/messages.py +18 -0
- llmir/py.typed +0 -0
- llmir/rich_repr.py +20 -0
- llmir/roles.py +9 -0
- llmir/tools.py +8 -0
- llmir-0.0.1.dist-info/METADATA +7 -0
- llmir-0.0.1.dist-info/RECORD +13 -0
- llmir-0.0.1.dist-info/WHEEL +5 -0
- llmir-0.0.1.dist-info/top_level.txt +1 -0
llmir/__init__.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from .chunks import AIChunk, AIChunkText, AIChunkFile, AIChunkImageURL, AIChunkToolCall
|
|
2
|
+
from .roles import AIRoles
|
|
3
|
+
from .messages import AIMessage, AIMessageToolResponse
|
|
4
|
+
from .tools import Tool
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"AIChunk",
|
|
8
|
+
"AIChunkText",
|
|
9
|
+
"AIChunkFile",
|
|
10
|
+
"AIChunkImageURL",
|
|
11
|
+
"AIChunkToolCall",
|
|
12
|
+
"AIRoles",
|
|
13
|
+
"AIMessage",
|
|
14
|
+
"AIMessageToolResponse",
|
|
15
|
+
|
|
16
|
+
"Tool",
|
|
17
|
+
]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from .openai import to_openai, OpenAIMessage, OpenAIMessageToolResponse, OpenAIContent, OpenAITextContent, OpenAIImageURLContent
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"to_openai",
|
|
5
|
+
"OpenAIMessage",
|
|
6
|
+
"OpenAIMessageToolResponse",
|
|
7
|
+
"OpenAIContent",
|
|
8
|
+
"OpenAITextContent",
|
|
9
|
+
"OpenAIImageURLContent",
|
|
10
|
+
]
|
llmir/adapter/openai.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
from typing import TypedDict, Literal, Union
|
|
2
|
+
|
|
3
|
+
from ..messages import AIMessage, AIMessageToolResponse
|
|
4
|
+
from ..chunks import AIChunk, AIChunkText, AIChunkImageURL, AIChunkFile
|
|
5
|
+
import base64
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OpenAITextContent(TypedDict):
|
|
9
|
+
type: Literal["text"]
|
|
10
|
+
text: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OpenAIImageURLContent(TypedDict):
|
|
14
|
+
type: Literal["image_url"]
|
|
15
|
+
image_url: dict[str, str]
|
|
16
|
+
|
|
17
|
+
OpenAIContent = Union[OpenAITextContent, OpenAIImageURLContent]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OpenAIMessage(TypedDict):
|
|
21
|
+
role: str
|
|
22
|
+
content: list[OpenAIContent]
|
|
23
|
+
|
|
24
|
+
class OpenAIMessageToolResponse(OpenAIMessage):
|
|
25
|
+
tool_call_id: str
|
|
26
|
+
name: str
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def to_openai(messages: list[AIMessage]) -> list[OpenAIMessage]:
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
result: list[OpenAIMessage] = []
|
|
33
|
+
for message in messages:
|
|
34
|
+
role = message.role.value
|
|
35
|
+
if isinstance(message, AIMessageToolResponse):
|
|
36
|
+
result.append(
|
|
37
|
+
OpenAIMessageToolResponse(
|
|
38
|
+
role=role,
|
|
39
|
+
tool_call_id=message.id,
|
|
40
|
+
name=message.name,
|
|
41
|
+
content=[
|
|
42
|
+
chunk_to_openai(chunk) for chunk in message.chunks
|
|
43
|
+
]
|
|
44
|
+
)
|
|
45
|
+
)
|
|
46
|
+
else:
|
|
47
|
+
result.append(OpenAIMessage(
|
|
48
|
+
role= role,
|
|
49
|
+
content= [
|
|
50
|
+
chunk_to_openai(chunk) for chunk in message.chunks
|
|
51
|
+
]
|
|
52
|
+
))
|
|
53
|
+
return result
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def chunk_to_openai(chunk: AIChunk) -> OpenAIContent:
|
|
57
|
+
|
|
58
|
+
match chunk:
|
|
59
|
+
case AIChunkText():
|
|
60
|
+
return OpenAITextContent(
|
|
61
|
+
type="text",
|
|
62
|
+
text=chunk.text,
|
|
63
|
+
)
|
|
64
|
+
case AIChunkImageURL():
|
|
65
|
+
return OpenAIImageURLContent(
|
|
66
|
+
type="image_url",
|
|
67
|
+
image_url={
|
|
68
|
+
"url": chunk.url,
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
case AIChunkFile():
|
|
72
|
+
if chunk.mimetype.startswith("image/"):
|
|
73
|
+
base64_data = base64.b64encode(chunk.bytes).decode('utf-8')
|
|
74
|
+
return OpenAIImageURLContent(
|
|
75
|
+
type= "image_url",
|
|
76
|
+
image_url= {
|
|
77
|
+
"url": f"data:{chunk.mimetype};base64,{base64_data}",
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
elif chunk.mimetype == ("text/plain"):
|
|
81
|
+
text = chunk.bytes.decode(encoding="utf-8")
|
|
82
|
+
return OpenAITextContent(
|
|
83
|
+
type="text",
|
|
84
|
+
text=text
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
raise ValueError(f"Unsupported file type for OpenAI: {chunk.mimetype}")
|
|
88
|
+
case _:
|
|
89
|
+
raise ValueError(f"Unsupported chunk type: {type(chunk)}")
|
llmir/chunks.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from typing import Union, Literal
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from .rich_repr import RichReprMixin
|
|
4
|
+
|
|
5
|
+
class AIChunkText(RichReprMixin, BaseModel):
|
|
6
|
+
type: Literal["text"] = "text"
|
|
7
|
+
text: str
|
|
8
|
+
|
|
9
|
+
class AIChunkFile(RichReprMixin, BaseModel):
|
|
10
|
+
type: Literal["file"] = "file"
|
|
11
|
+
name: str
|
|
12
|
+
mimetype: str
|
|
13
|
+
bytes: bytes
|
|
14
|
+
|
|
15
|
+
class AIChunkImageURL(RichReprMixin, BaseModel):
|
|
16
|
+
type: Literal["image"] = "image"
|
|
17
|
+
url: str
|
|
18
|
+
|
|
19
|
+
class AIChunkToolCall(RichReprMixin, BaseModel):
|
|
20
|
+
type: Literal["tool_call"] = "tool_call"
|
|
21
|
+
id: str
|
|
22
|
+
name: str
|
|
23
|
+
arguments: dict[str, object]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
AIChunk = Union[AIChunkText, AIChunkFile, AIChunkImageURL, AIChunkToolCall]
|
llmir/messages.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
from .chunks import AIChunk
|
|
4
|
+
from .roles import AIRoles
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class AIMessage(BaseModel):
|
|
8
|
+
|
|
9
|
+
role: AIRoles
|
|
10
|
+
chunks: list[AIChunk] = Field(default_factory=list[AIChunk])
|
|
11
|
+
|
|
12
|
+
class AIMessageToolResponse(AIMessage):
|
|
13
|
+
|
|
14
|
+
id: str
|
|
15
|
+
name: str
|
|
16
|
+
role: AIRoles = AIRoles.TOOL
|
|
17
|
+
|
|
18
|
+
|
llmir/py.typed
ADDED
|
File without changes
|
llmir/rich_repr.py
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
class RichReprMixin(BaseModel):
|
|
4
|
+
|
|
5
|
+
MAX_CHARS_PER_VALUE: int = Field(default=2000, exclude=True)
|
|
6
|
+
|
|
7
|
+
def __rich_repr__(self):
|
|
8
|
+
|
|
9
|
+
for name, field_info in self.__class__.model_fields.items():
|
|
10
|
+
|
|
11
|
+
if field_info.exclude:
|
|
12
|
+
continue
|
|
13
|
+
|
|
14
|
+
value = getattr(self, name)
|
|
15
|
+
length = len(str(value))
|
|
16
|
+
|
|
17
|
+
if length > self.MAX_CHARS_PER_VALUE:
|
|
18
|
+
yield name, f"<object of length: {len(str(value))}>"
|
|
19
|
+
else:
|
|
20
|
+
yield name, value
|
llmir/roles.py
ADDED
llmir/tools.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
llmir/__init__.py,sha256=mxV4TWB8xrR8qa3y5gfQZn-YfKRQTYWsPVvyeIjz7js,381
|
|
2
|
+
llmir/chunks.py,sha256=lnN7-3kjYalM6YO0v4ZM2-SYDNZDFfrergj3KEzcv0o,658
|
|
3
|
+
llmir/messages.py,sha256=G_F_gLGvBqzPZbni0iaWAMYG3-cDPJCeLJL2-9ZcAqI,311
|
|
4
|
+
llmir/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
llmir/rich_repr.py,sha256=ZGAfpcPkYJSUpFdir8B1MoORVFBSzVckCrRHVVkJIj4,559
|
|
6
|
+
llmir/roles.py,sha256=B9PmxTHAb8XoiA9en_J1yIP19woddzx4SRUz5XkUxFc,133
|
|
7
|
+
llmir/tools.py,sha256=fpkC3IafHqZJnwo7imtHdkTdGLBHQuR6DtdztXZrlWg,147
|
|
8
|
+
llmir/adapter/__init__.py,sha256=2DKTM3v35_UPVTKiseo6rtIHj_P2OdUgxhNY_PKs5K8,289
|
|
9
|
+
llmir/adapter/openai.py,sha256=_DRbBWhTYUdSEsdCTyn-FraqCQ5Ke2XRnHotJHAWyGM,2644
|
|
10
|
+
llmir-0.0.1.dist-info/METADATA,sha256=Oh-jOxYnQTw17bXZWhkJ3T8fENnEhUIUPCG-UiT_EZg,177
|
|
11
|
+
llmir-0.0.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
llmir-0.0.1.dist-info/top_level.txt,sha256=fm538OsNUSYa_71IPztSSN2PBCvcbaD5PmUIzaO5hQs,6
|
|
13
|
+
llmir-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
llmir
|