lionagi 0.17.10__py3-none-any.whl → 0.18.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.
- lionagi/__init__.py +1 -2
- lionagi/_class_registry.py +1 -2
- lionagi/_errors.py +1 -2
- lionagi/adapters/async_postgres_adapter.py +2 -10
- lionagi/config.py +1 -2
- lionagi/fields/action.py +1 -2
- lionagi/fields/base.py +3 -0
- lionagi/fields/code.py +3 -0
- lionagi/fields/file.py +3 -0
- lionagi/fields/instruct.py +1 -2
- lionagi/fields/reason.py +1 -2
- lionagi/fields/research.py +3 -0
- lionagi/libs/__init__.py +1 -2
- lionagi/libs/file/__init__.py +1 -2
- lionagi/libs/file/chunk.py +1 -2
- lionagi/libs/file/process.py +1 -2
- lionagi/libs/schema/__init__.py +1 -2
- lionagi/libs/schema/as_readable.py +1 -2
- lionagi/libs/schema/extract_code_block.py +1 -2
- lionagi/libs/schema/extract_docstring.py +1 -2
- lionagi/libs/schema/function_to_schema.py +1 -2
- lionagi/libs/schema/load_pydantic_model_from_schema.py +1 -2
- lionagi/libs/schema/minimal_yaml.py +98 -0
- lionagi/libs/validate/__init__.py +1 -2
- lionagi/libs/validate/common_field_validators.py +1 -2
- lionagi/libs/validate/validate_boolean.py +1 -2
- lionagi/ln/fuzzy/_string_similarity.py +1 -2
- lionagi/ln/types.py +32 -5
- lionagi/models/__init__.py +1 -2
- lionagi/models/field_model.py +9 -1
- lionagi/models/hashable_model.py +4 -2
- lionagi/models/model_params.py +1 -2
- lionagi/models/operable_model.py +1 -2
- lionagi/models/schema_model.py +1 -2
- lionagi/operations/ReAct/ReAct.py +475 -239
- lionagi/operations/ReAct/__init__.py +1 -2
- lionagi/operations/ReAct/utils.py +4 -2
- lionagi/operations/__init__.py +1 -2
- lionagi/operations/act/__init__.py +2 -0
- lionagi/operations/act/act.py +206 -0
- lionagi/operations/brainstorm/__init__.py +1 -2
- lionagi/operations/brainstorm/brainstorm.py +1 -2
- lionagi/operations/brainstorm/prompt.py +1 -2
- lionagi/operations/builder.py +1 -2
- lionagi/operations/chat/__init__.py +1 -2
- lionagi/operations/chat/chat.py +131 -116
- lionagi/operations/communicate/communicate.py +102 -44
- lionagi/operations/flow.py +5 -6
- lionagi/operations/instruct/__init__.py +1 -2
- lionagi/operations/instruct/instruct.py +1 -2
- lionagi/operations/interpret/__init__.py +1 -2
- lionagi/operations/interpret/interpret.py +66 -22
- lionagi/operations/operate/__init__.py +1 -2
- lionagi/operations/operate/operate.py +213 -108
- lionagi/operations/parse/__init__.py +1 -2
- lionagi/operations/parse/parse.py +171 -144
- lionagi/operations/plan/__init__.py +1 -2
- lionagi/operations/plan/plan.py +1 -2
- lionagi/operations/plan/prompt.py +1 -2
- lionagi/operations/select/__init__.py +1 -2
- lionagi/operations/select/select.py +79 -19
- lionagi/operations/select/utils.py +2 -3
- lionagi/operations/types.py +120 -25
- lionagi/operations/utils.py +1 -2
- lionagi/protocols/__init__.py +1 -2
- lionagi/protocols/_concepts.py +1 -2
- lionagi/protocols/action/__init__.py +1 -2
- lionagi/protocols/action/function_calling.py +3 -20
- lionagi/protocols/action/manager.py +34 -4
- lionagi/protocols/action/tool.py +1 -2
- lionagi/protocols/contracts.py +1 -2
- lionagi/protocols/forms/__init__.py +1 -2
- lionagi/protocols/forms/base.py +1 -2
- lionagi/protocols/forms/flow.py +1 -2
- lionagi/protocols/forms/form.py +1 -2
- lionagi/protocols/forms/report.py +1 -2
- lionagi/protocols/generic/__init__.py +1 -2
- lionagi/protocols/generic/element.py +17 -65
- lionagi/protocols/generic/event.py +1 -2
- lionagi/protocols/generic/log.py +17 -14
- lionagi/protocols/generic/pile.py +3 -4
- lionagi/protocols/generic/processor.py +1 -2
- lionagi/protocols/generic/progression.py +1 -2
- lionagi/protocols/graph/__init__.py +1 -2
- lionagi/protocols/graph/edge.py +1 -2
- lionagi/protocols/graph/graph.py +1 -2
- lionagi/protocols/graph/node.py +1 -2
- lionagi/protocols/ids.py +1 -2
- lionagi/protocols/mail/__init__.py +1 -2
- lionagi/protocols/mail/exchange.py +1 -2
- lionagi/protocols/mail/mail.py +1 -2
- lionagi/protocols/mail/mailbox.py +1 -2
- lionagi/protocols/mail/manager.py +1 -2
- lionagi/protocols/mail/package.py +1 -2
- lionagi/protocols/messages/__init__.py +28 -2
- lionagi/protocols/messages/action_request.py +87 -186
- lionagi/protocols/messages/action_response.py +74 -133
- lionagi/protocols/messages/assistant_response.py +131 -161
- lionagi/protocols/messages/base.py +27 -20
- lionagi/protocols/messages/instruction.py +281 -626
- lionagi/protocols/messages/manager.py +113 -64
- lionagi/protocols/messages/message.py +88 -199
- lionagi/protocols/messages/system.py +53 -125
- lionagi/protocols/operatives/__init__.py +1 -2
- lionagi/protocols/operatives/operative.py +1 -2
- lionagi/protocols/operatives/step.py +1 -2
- lionagi/protocols/types.py +1 -4
- lionagi/service/connections/__init__.py +1 -2
- lionagi/service/connections/api_calling.py +1 -2
- lionagi/service/connections/endpoint.py +1 -10
- lionagi/service/connections/endpoint_config.py +1 -2
- lionagi/service/connections/header_factory.py +1 -2
- lionagi/service/connections/match_endpoint.py +1 -2
- lionagi/service/connections/mcp/__init__.py +1 -2
- lionagi/service/connections/mcp/wrapper.py +1 -2
- lionagi/service/connections/providers/__init__.py +1 -2
- lionagi/service/connections/providers/anthropic_.py +1 -2
- lionagi/service/connections/providers/claude_code_cli.py +1 -2
- lionagi/service/connections/providers/exa_.py +1 -2
- lionagi/service/connections/providers/nvidia_nim_.py +2 -27
- lionagi/service/connections/providers/oai_.py +30 -96
- lionagi/service/connections/providers/ollama_.py +4 -4
- lionagi/service/connections/providers/perplexity_.py +1 -2
- lionagi/service/hooks/__init__.py +1 -1
- lionagi/service/hooks/_types.py +1 -1
- lionagi/service/hooks/_utils.py +1 -1
- lionagi/service/hooks/hook_event.py +1 -1
- lionagi/service/hooks/hook_registry.py +1 -1
- lionagi/service/hooks/hooked_event.py +3 -4
- lionagi/service/imodel.py +1 -2
- lionagi/service/manager.py +1 -2
- lionagi/service/rate_limited_processor.py +1 -2
- lionagi/service/resilience.py +1 -2
- lionagi/service/third_party/anthropic_models.py +1 -2
- lionagi/service/third_party/claude_code.py +4 -4
- lionagi/service/third_party/openai_models.py +433 -0
- lionagi/service/token_calculator.py +1 -2
- lionagi/session/__init__.py +1 -2
- lionagi/session/branch.py +171 -180
- lionagi/session/session.py +4 -11
- lionagi/tools/__init__.py +1 -2
- lionagi/tools/base.py +1 -2
- lionagi/tools/file/__init__.py +1 -2
- lionagi/tools/file/reader.py +3 -4
- lionagi/tools/types.py +1 -2
- lionagi/utils.py +1 -2
- lionagi/version.py +1 -1
- {lionagi-0.17.10.dist-info → lionagi-0.18.0.dist-info}/METADATA +1 -2
- lionagi-0.18.0.dist-info/RECORD +191 -0
- lionagi/operations/_act/__init__.py +0 -3
- lionagi/operations/_act/act.py +0 -87
- lionagi/protocols/messages/templates/README.md +0 -28
- lionagi/protocols/messages/templates/action_request.jinja2 +0 -5
- lionagi/protocols/messages/templates/action_response.jinja2 +0 -9
- lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -6
- lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -61
- lionagi/protocols/messages/templates/system_message.jinja2 +0 -11
- lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -7
- lionagi/service/connections/providers/types.py +0 -28
- lionagi/service/third_party/openai_model_names.py +0 -198
- lionagi/service/types.py +0 -59
- lionagi-0.17.10.dist-info/RECORD +0 -199
- {lionagi-0.17.10.dist-info → lionagi-0.18.0.dist-info}/WHEEL +0 -0
- {lionagi-0.17.10.dist-info → lionagi-0.18.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,208 +1,178 @@
|
|
1
|
-
# Copyright (c) 2023
|
2
|
-
#
|
1
|
+
# Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
|
3
2
|
# SPDX-License-Identifier: Apache-2.0
|
4
3
|
|
4
|
+
from dataclasses import dataclass
|
5
5
|
from typing import Any
|
6
6
|
|
7
|
-
from pydantic import BaseModel
|
7
|
+
from pydantic import BaseModel, field_validator
|
8
8
|
|
9
|
-
from
|
9
|
+
from .base import SenderRecipient
|
10
|
+
from .message import MessageContent, MessageRole, RoledMessage
|
10
11
|
|
11
|
-
from .base import MessageRole, SenderRecipient
|
12
|
-
from .message import MessageRole, RoledMessage, Template, jinja_env
|
13
12
|
|
13
|
+
def parse_assistant_response(
|
14
|
+
response: BaseModel | list[BaseModel] | dict | str | Any,
|
15
|
+
) -> tuple[str, dict | list[dict]]:
|
16
|
+
"""Parse various AI model response formats into text and raw data.
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
Supports:
|
19
|
+
- Anthropic format (content field)
|
20
|
+
- OpenAI chat completions (choices field)
|
21
|
+
- OpenAI responses API (output field)
|
22
|
+
- Claude Code (result field)
|
23
|
+
- Raw strings
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
tuple: (extracted_text, raw_model_response)
|
27
|
+
"""
|
28
|
+
responses = [response] if not isinstance(response, list) else response
|
23
29
|
|
24
30
|
text_contents = []
|
25
31
|
model_responses = []
|
26
32
|
|
27
|
-
for
|
28
|
-
if isinstance(
|
29
|
-
|
33
|
+
for item in responses:
|
34
|
+
if isinstance(item, BaseModel):
|
35
|
+
item = item.model_dump(exclude_none=True, exclude_unset=True)
|
30
36
|
|
31
|
-
model_responses.append(
|
37
|
+
model_responses.append(item)
|
32
38
|
|
33
|
-
if isinstance(
|
34
|
-
#
|
35
|
-
if "content" in
|
36
|
-
content =
|
39
|
+
if isinstance(item, dict):
|
40
|
+
# Anthropic standard
|
41
|
+
if "content" in item:
|
42
|
+
content = item["content"]
|
37
43
|
content = (
|
38
44
|
[content] if not isinstance(content, list) else content
|
39
45
|
)
|
40
|
-
for
|
41
|
-
if isinstance(
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
choices = i["choices"]
|
46
|
+
for c in content:
|
47
|
+
if isinstance(c, dict) and c.get("type") == "text":
|
48
|
+
text_contents.append(c["text"])
|
49
|
+
elif isinstance(c, str):
|
50
|
+
text_contents.append(c)
|
51
|
+
|
52
|
+
# OpenAI chat completions standard
|
53
|
+
elif "choices" in item:
|
54
|
+
choices = item["choices"]
|
50
55
|
choices = (
|
51
56
|
[choices] if not isinstance(choices, list) else choices
|
52
57
|
)
|
53
|
-
for
|
54
|
-
if "message" in
|
55
|
-
text_contents.append(
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
for choice in choices:
|
59
|
+
if "message" in choice:
|
60
|
+
text_contents.append(
|
61
|
+
choice["message"].get("content") or ""
|
62
|
+
)
|
63
|
+
elif "delta" in choice:
|
64
|
+
text_contents.append(
|
65
|
+
choice["delta"].get("content") or ""
|
66
|
+
)
|
67
|
+
|
68
|
+
# OpenAI responses API standard
|
69
|
+
elif "output" in item:
|
70
|
+
output = item["output"]
|
62
71
|
output = [output] if not isinstance(output, list) else output
|
63
|
-
for
|
64
|
-
if isinstance(
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
text_contents = "".join(text_contents)
|
86
|
-
model_responses = (
|
72
|
+
for out in output:
|
73
|
+
if isinstance(out, dict) and out.get("type") == "message":
|
74
|
+
content = out.get("content", [])
|
75
|
+
if isinstance(content, list):
|
76
|
+
for c in content:
|
77
|
+
if (
|
78
|
+
isinstance(c, dict)
|
79
|
+
and c.get("type") == "output_text"
|
80
|
+
):
|
81
|
+
text_contents.append(c.get("text", ""))
|
82
|
+
elif isinstance(c, str):
|
83
|
+
text_contents.append(c)
|
84
|
+
|
85
|
+
# Claude Code standard
|
86
|
+
elif "result" in item:
|
87
|
+
text_contents.append(item["result"])
|
88
|
+
|
89
|
+
elif isinstance(item, str):
|
90
|
+
text_contents.append(item)
|
91
|
+
|
92
|
+
text = "".join(text_contents)
|
93
|
+
model_response = (
|
87
94
|
model_responses[0] if len(model_responses) == 1 else model_responses
|
88
95
|
)
|
89
|
-
return {
|
90
|
-
"assistant_response": text_contents,
|
91
|
-
"model_response": model_responses,
|
92
|
-
}
|
93
96
|
|
97
|
+
return text, model_response
|
94
98
|
|
95
|
-
|
99
|
+
|
100
|
+
@dataclass(slots=True)
|
101
|
+
class AssistantResponseContent(MessageContent):
|
102
|
+
"""Content for assistant responses.
|
103
|
+
|
104
|
+
Fields:
|
105
|
+
assistant_response: Extracted text from the model
|
96
106
|
"""
|
97
|
-
|
98
|
-
|
99
|
-
|
107
|
+
|
108
|
+
assistant_response: str = ""
|
109
|
+
|
110
|
+
@property
|
111
|
+
def rendered(self) -> str:
|
112
|
+
"""Render assistant response as plain text."""
|
113
|
+
return self.assistant_response
|
114
|
+
|
115
|
+
@classmethod
|
116
|
+
def from_dict(cls, data: dict[str, Any]) -> "AssistantResponseContent":
|
117
|
+
"""Construct AssistantResponseContent from dictionary."""
|
118
|
+
assistant_response = data.get("assistant_response", "")
|
119
|
+
return cls(assistant_response=assistant_response)
|
120
|
+
|
121
|
+
|
122
|
+
class AssistantResponse(RoledMessage):
|
123
|
+
"""Message representing an AI assistant's reply.
|
124
|
+
|
125
|
+
The raw model output is stored in metadata["model_response"].
|
100
126
|
"""
|
101
127
|
|
102
|
-
|
103
|
-
|
104
|
-
|
128
|
+
role: MessageRole = MessageRole.ASSISTANT
|
129
|
+
content: AssistantResponseContent
|
130
|
+
recipient: SenderRecipient | None = MessageRole.USER
|
131
|
+
|
132
|
+
@field_validator("content", mode="before")
|
133
|
+
def _validate_content(cls, v):
|
134
|
+
if v is None:
|
135
|
+
return AssistantResponseContent()
|
136
|
+
if isinstance(v, dict):
|
137
|
+
return AssistantResponseContent.from_dict(v)
|
138
|
+
if isinstance(v, AssistantResponseContent):
|
139
|
+
return v
|
140
|
+
raise TypeError(
|
141
|
+
"content must be dict or AssistantResponseContent instance"
|
142
|
+
)
|
105
143
|
|
106
144
|
@property
|
107
145
|
def response(self) -> str:
|
108
|
-
"""
|
109
|
-
return
|
110
|
-
|
111
|
-
@response.setter
|
112
|
-
def response(self, value: str) -> None:
|
113
|
-
self.content["assistant_response"] = value
|
146
|
+
"""Access the text response from the assistant."""
|
147
|
+
return self.content.assistant_response
|
114
148
|
|
115
149
|
@property
|
116
150
|
def model_response(self) -> dict | list[dict]:
|
117
|
-
"""
|
118
|
-
|
119
|
-
|
120
|
-
Returns:
|
121
|
-
dict or list[dict]: The stored model output data.
|
122
|
-
"""
|
123
|
-
return copy(self.metadata.get("model_response", {}))
|
151
|
+
"""Access the underlying model's raw data from metadata."""
|
152
|
+
return self.metadata.get("model_response", {})
|
124
153
|
|
125
154
|
@classmethod
|
126
|
-
def
|
155
|
+
def from_response(
|
127
156
|
cls,
|
128
|
-
|
157
|
+
response: BaseModel | list[BaseModel] | dict | str | Any,
|
129
158
|
sender: SenderRecipient | None = None,
|
130
159
|
recipient: SenderRecipient | None = None,
|
131
|
-
template: Template | str | None = None,
|
132
|
-
**kwargs,
|
133
160
|
) -> "AssistantResponse":
|
134
|
-
"""
|
135
|
-
Build an AssistantResponse from arbitrary assistant data.
|
161
|
+
"""Create AssistantResponse from raw model output.
|
136
162
|
|
137
163
|
Args:
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
sender (SenderRecipient | None):
|
142
|
-
The ID or role denoting who sends this response.
|
143
|
-
recipient (SenderRecipient | None):
|
144
|
-
The ID or role to receive it.
|
145
|
-
template (Template | str | None):
|
146
|
-
Optional custom template.
|
147
|
-
**kwargs:
|
148
|
-
Additional content key-value pairs.
|
164
|
+
response: Raw model output in any supported format
|
165
|
+
sender: Message sender
|
166
|
+
recipient: Message recipient
|
149
167
|
|
150
168
|
Returns:
|
151
|
-
AssistantResponse
|
169
|
+
AssistantResponse with parsed content and metadata
|
152
170
|
"""
|
153
|
-
|
154
|
-
model_response = content.pop("model_response", {})
|
155
|
-
content.update(kwargs)
|
156
|
-
params = {
|
157
|
-
"content": content,
|
158
|
-
"role": MessageRole.ASSISTANT,
|
159
|
-
"recipient": recipient or MessageRole.USER,
|
160
|
-
}
|
161
|
-
if sender:
|
162
|
-
params["sender"] = sender
|
163
|
-
if template:
|
164
|
-
params["template"] = template
|
165
|
-
if model_response:
|
166
|
-
params["metadata"] = {"model_response": model_response}
|
167
|
-
return cls(**params)
|
168
|
-
|
169
|
-
def update(
|
170
|
-
self,
|
171
|
-
assistant_response: (
|
172
|
-
BaseModel | list[BaseModel] | dict | str | Any
|
173
|
-
) = None,
|
174
|
-
sender: SenderRecipient | None = None,
|
175
|
-
recipient: SenderRecipient | None = None,
|
176
|
-
template: Template | str | None = None,
|
177
|
-
**kwargs,
|
178
|
-
):
|
179
|
-
"""
|
180
|
-
Update this AssistantResponse with new data or fields.
|
171
|
+
text, model_response = parse_assistant_response(response)
|
181
172
|
|
182
|
-
|
183
|
-
assistant_response
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
recipient (SenderRecipient | None):
|
188
|
-
Updated recipient.
|
189
|
-
template (Template | str | None):
|
190
|
-
Optional new template.
|
191
|
-
**kwargs:
|
192
|
-
Additional content updates for `self.content`.
|
193
|
-
"""
|
194
|
-
if assistant_response:
|
195
|
-
content = prepare_assistant_response(assistant_response)
|
196
|
-
self.content.update(content)
|
197
|
-
super().update(
|
198
|
-
sender=sender, recipient=recipient, template=template, **kwargs
|
173
|
+
return cls(
|
174
|
+
content=AssistantResponseContent(assistant_response=text),
|
175
|
+
sender=sender,
|
176
|
+
recipient=recipient or MessageRole.USER,
|
177
|
+
metadata={"model_response": model_response},
|
199
178
|
)
|
200
|
-
|
201
|
-
def as_context(self) -> dict:
|
202
|
-
return f"""
|
203
|
-
Response: {self.response or "Not available"}
|
204
|
-
Summary: {self.model_response.get("summary") or "Not available"}
|
205
|
-
""".strip()
|
206
|
-
|
207
|
-
|
208
|
-
# File: lionagi/protocols/messages/assistant_response.py
|
@@ -1,18 +1,17 @@
|
|
1
|
-
# Copyright (c) 2023
|
2
|
-
#
|
1
|
+
# Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
|
3
2
|
# SPDX-License-Identifier: Apache-2.0
|
4
3
|
|
5
4
|
from enum import Enum
|
6
5
|
from typing import Any, TypeAlias
|
7
6
|
|
8
|
-
from ..generic.element import ID, IDError, IDType, Observable
|
7
|
+
from ..generic.element import ID, Element, IDError, IDType, Observable
|
9
8
|
|
10
9
|
__all__ = (
|
11
10
|
"MessageRole",
|
12
|
-
"MessageFlag",
|
13
11
|
"MessageField",
|
14
12
|
"MESSAGE_FIELDS",
|
15
13
|
"validate_sender_recipient",
|
14
|
+
"serialize_sender_recipient",
|
16
15
|
)
|
17
16
|
|
18
17
|
|
@@ -28,15 +27,6 @@ class MessageRole(str, Enum):
|
|
28
27
|
ACTION = "action"
|
29
28
|
|
30
29
|
|
31
|
-
class MessageFlag(str, Enum):
|
32
|
-
"""
|
33
|
-
Internal flags for certain message states, e.g., clones or loads.
|
34
|
-
"""
|
35
|
-
|
36
|
-
MESSAGE_CLONE = "MESSAGE_CLONE"
|
37
|
-
MESSAGE_LOAD = "MESSAGE_LOAD"
|
38
|
-
|
39
|
-
|
40
30
|
SenderRecipient: TypeAlias = IDType | MessageRole | str
|
41
31
|
"""
|
42
32
|
A union type indicating that a sender or recipient could be:
|
@@ -76,7 +66,7 @@ def validate_sender_recipient(value: Any, /) -> SenderRecipient:
|
|
76
66
|
Raises:
|
77
67
|
ValueError: If the input cannot be recognized as a role or ID.
|
78
68
|
"""
|
79
|
-
if isinstance(value, MessageRole
|
69
|
+
if isinstance(value, MessageRole):
|
80
70
|
return value
|
81
71
|
|
82
72
|
if isinstance(value, IDType):
|
@@ -91,13 +81,30 @@ def validate_sender_recipient(value: Any, /) -> SenderRecipient:
|
|
91
81
|
if value in ["system", "user", "unset", "assistant", "action"]:
|
92
82
|
return MessageRole(value)
|
93
83
|
|
94
|
-
|
95
|
-
|
84
|
+
# Accept plain strings (user names, identifiers, etc)
|
85
|
+
if isinstance(value, str):
|
86
|
+
# Try to parse as ID first, but allow plain strings as fallback
|
87
|
+
try:
|
88
|
+
return ID.get_id(value)
|
89
|
+
except IDError:
|
90
|
+
return value
|
96
91
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
92
|
+
raise ValueError("Invalid sender or recipient")
|
93
|
+
|
94
|
+
|
95
|
+
def serialize_sender_recipient(value: Any) -> str | None:
|
96
|
+
if not value:
|
97
|
+
return None
|
98
|
+
# Check instance types first before enum membership
|
99
|
+
if isinstance(value, Element):
|
100
|
+
return str(value.id)
|
101
|
+
if isinstance(value, IDType):
|
102
|
+
return str(value)
|
103
|
+
if isinstance(value, MessageRole):
|
104
|
+
return value.value
|
105
|
+
if isinstance(value, str):
|
106
|
+
return value
|
107
|
+
return str(value)
|
101
108
|
|
102
109
|
|
103
110
|
# File: lionagi/protocols/messages/base.py
|