lionagi 0.17.11__py3-none-any.whl → 0.18.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.
Files changed (109) hide show
  1. lionagi/_errors.py +0 -5
  2. lionagi/fields.py +83 -0
  3. lionagi/libs/schema/minimal_yaml.py +98 -0
  4. lionagi/ln/__init__.py +3 -1
  5. lionagi/ln/concurrency/primitives.py +4 -4
  6. lionagi/ln/concurrency/task.py +1 -0
  7. lionagi/ln/types.py +32 -5
  8. lionagi/models/field_model.py +21 -4
  9. lionagi/models/hashable_model.py +2 -3
  10. lionagi/operations/ReAct/ReAct.py +475 -238
  11. lionagi/operations/ReAct/utils.py +3 -0
  12. lionagi/operations/act/act.py +206 -0
  13. lionagi/operations/builder.py +5 -7
  14. lionagi/operations/chat/chat.py +130 -114
  15. lionagi/operations/communicate/communicate.py +101 -42
  16. lionagi/operations/fields.py +380 -0
  17. lionagi/operations/flow.py +8 -10
  18. lionagi/operations/interpret/interpret.py +65 -20
  19. lionagi/operations/node.py +4 -4
  20. lionagi/operations/operate/operate.py +216 -108
  21. lionagi/{protocols/operatives → operations/operate}/operative.py +4 -5
  22. lionagi/{protocols/operatives → operations/operate}/step.py +34 -39
  23. lionagi/operations/parse/parse.py +170 -142
  24. lionagi/operations/select/select.py +79 -18
  25. lionagi/operations/select/utils.py +8 -2
  26. lionagi/operations/types.py +119 -23
  27. lionagi/protocols/action/manager.py +5 -6
  28. lionagi/protocols/contracts.py +2 -2
  29. lionagi/protocols/generic/__init__.py +22 -0
  30. lionagi/protocols/generic/element.py +36 -127
  31. lionagi/protocols/generic/log.py +3 -2
  32. lionagi/protocols/generic/pile.py +9 -10
  33. lionagi/protocols/generic/progression.py +23 -22
  34. lionagi/protocols/graph/edge.py +6 -5
  35. lionagi/protocols/ids.py +6 -49
  36. lionagi/protocols/messages/__init__.py +29 -0
  37. lionagi/protocols/messages/action_request.py +86 -184
  38. lionagi/protocols/messages/action_response.py +73 -131
  39. lionagi/protocols/messages/assistant_response.py +130 -159
  40. lionagi/protocols/messages/base.py +31 -22
  41. lionagi/protocols/messages/instruction.py +280 -625
  42. lionagi/protocols/messages/manager.py +112 -62
  43. lionagi/protocols/messages/message.py +87 -197
  44. lionagi/protocols/messages/system.py +52 -123
  45. lionagi/protocols/types.py +1 -13
  46. lionagi/service/connections/__init__.py +3 -0
  47. lionagi/service/connections/endpoint.py +0 -8
  48. lionagi/service/connections/providers/claude_code_cli.py +3 -2
  49. lionagi/service/connections/providers/oai_.py +29 -94
  50. lionagi/service/connections/providers/ollama_.py +3 -2
  51. lionagi/service/hooks/_types.py +1 -1
  52. lionagi/service/hooks/_utils.py +1 -1
  53. lionagi/service/hooks/hook_event.py +3 -8
  54. lionagi/service/hooks/hook_registry.py +5 -5
  55. lionagi/service/hooks/hooked_event.py +63 -3
  56. lionagi/service/imodel.py +24 -20
  57. lionagi/service/third_party/claude_code.py +3 -3
  58. lionagi/service/third_party/openai_models.py +435 -0
  59. lionagi/service/token_calculator.py +1 -94
  60. lionagi/session/branch.py +190 -400
  61. lionagi/session/session.py +8 -99
  62. lionagi/tools/file/reader.py +2 -2
  63. lionagi/version.py +1 -1
  64. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/METADATA +6 -6
  65. lionagi-0.18.1.dist-info/RECORD +164 -0
  66. lionagi/fields/__init__.py +0 -47
  67. lionagi/fields/action.py +0 -188
  68. lionagi/fields/base.py +0 -153
  69. lionagi/fields/code.py +0 -239
  70. lionagi/fields/file.py +0 -234
  71. lionagi/fields/instruct.py +0 -135
  72. lionagi/fields/reason.py +0 -55
  73. lionagi/fields/research.py +0 -52
  74. lionagi/operations/_act/act.py +0 -86
  75. lionagi/operations/brainstorm/__init__.py +0 -2
  76. lionagi/operations/brainstorm/brainstorm.py +0 -498
  77. lionagi/operations/brainstorm/prompt.py +0 -11
  78. lionagi/operations/instruct/__init__.py +0 -2
  79. lionagi/operations/instruct/instruct.py +0 -28
  80. lionagi/operations/plan/__init__.py +0 -6
  81. lionagi/operations/plan/plan.py +0 -386
  82. lionagi/operations/plan/prompt.py +0 -25
  83. lionagi/operations/utils.py +0 -45
  84. lionagi/protocols/forms/__init__.py +0 -2
  85. lionagi/protocols/forms/base.py +0 -85
  86. lionagi/protocols/forms/flow.py +0 -79
  87. lionagi/protocols/forms/form.py +0 -86
  88. lionagi/protocols/forms/report.py +0 -48
  89. lionagi/protocols/mail/__init__.py +0 -2
  90. lionagi/protocols/mail/exchange.py +0 -220
  91. lionagi/protocols/mail/mail.py +0 -51
  92. lionagi/protocols/mail/mailbox.py +0 -103
  93. lionagi/protocols/mail/manager.py +0 -218
  94. lionagi/protocols/mail/package.py +0 -101
  95. lionagi/protocols/messages/templates/README.md +0 -28
  96. lionagi/protocols/messages/templates/action_request.jinja2 +0 -5
  97. lionagi/protocols/messages/templates/action_response.jinja2 +0 -9
  98. lionagi/protocols/messages/templates/assistant_response.jinja2 +0 -6
  99. lionagi/protocols/messages/templates/instruction_message.jinja2 +0 -61
  100. lionagi/protocols/messages/templates/system_message.jinja2 +0 -11
  101. lionagi/protocols/messages/templates/tool_schemas.jinja2 +0 -7
  102. lionagi/protocols/operatives/__init__.py +0 -2
  103. lionagi/service/connections/providers/types.py +0 -28
  104. lionagi/service/third_party/openai_model_names.py +0 -198
  105. lionagi/service/types.py +0 -58
  106. lionagi-0.17.11.dist-info/RECORD +0 -199
  107. /lionagi/operations/{_act → act}/__init__.py +0 -0
  108. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/WHEEL +0 -0
  109. {lionagi-0.17.11.dist-info → lionagi-0.18.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,218 +0,0 @@
1
- # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
2
- # SPDX-License-Identifier: Apache-2.0
3
-
4
- import asyncio
5
- from collections import deque
6
- from typing import Any
7
-
8
- from lionagi._errors import ItemNotFoundError
9
-
10
- from .._concepts import Manager, Observable
11
- from ..generic.element import ID, IDType
12
- from ..generic.pile import Pile, to_list_type
13
- from .exchange import Exchange
14
- from .mail import Mail, Package, PackageCategory
15
-
16
-
17
- class MailManager(Manager):
18
- """
19
- A manager for mail operations across various observable sources
20
- within LionAGI. Unlike `Exchange`, this class can manage the state
21
- of multiple sources in a more general or higher-level context,
22
- storing mail queues in a dictionary rather than individual buffers.
23
-
24
- Attributes
25
- ----------
26
- sources : Pile[Observable]
27
- A concurrency-safe collection of known sources.
28
- mails : dict[str, dict[str, deque]]
29
- A nested mapping of recipient -> sender -> queue of mail.
30
- execute_stop : bool
31
- Controls the asynchronous execution loop; set to True to exit.
32
- """
33
-
34
- def __init__(self, sources: ID.Item | ID.ItemSeq = None) -> None:
35
- """
36
- Initialize a MailManager instance.
37
-
38
- Parameters
39
- ----------
40
- sources : ID.Item | ID.ItemSeq, optional
41
- Initial source(s) to manage. Each source must be an Observable.
42
- """
43
- self.sources: Pile[Observable] = Pile()
44
- self.mails: dict[str, dict[str, deque]] = {}
45
- self.execute_stop: bool = False
46
-
47
- if sources:
48
- self.add_sources(sources)
49
-
50
- def add_sources(self, sources: ID.Item | ID.ItemSeq, /) -> None:
51
- """
52
- Register new sources in the MailManager.
53
-
54
- Parameters
55
- ----------
56
- sources : ID.Item | ID.ItemSeq
57
- A single source or multiple sources to be added.
58
-
59
- Raises
60
- ------
61
- ValueError
62
- If adding the sources fails for any reason.
63
- """
64
- try:
65
- sources = to_list_type(sources)
66
- self.sources.include(sources)
67
- for item in sources:
68
- self.mails[item.id] = {}
69
- except Exception as e:
70
- raise ValueError("Failed to add source.") from e
71
-
72
- @staticmethod
73
- def create_mail(
74
- sender: ID.Ref,
75
- recipient: ID.Ref,
76
- category: PackageCategory | str,
77
- package: Any,
78
- request_source: Any = None,
79
- ) -> Mail:
80
- """
81
- Factory method to generate a Mail object.
82
-
83
- Parameters
84
- ----------
85
- sender : ID.Ref
86
- Reference (ID or object) for the sender.
87
- recipient : ID.Ref
88
- Reference (ID or object) for the recipient.
89
- category : PackageCategory | str
90
- The category of this package.
91
- package : Any
92
- The payload or content in the mail.
93
- request_source : Any, optional
94
- Additional context about the request source.
95
-
96
- Returns
97
- -------
98
- Mail
99
- A new mail object with specified sender, recipient, and package.
100
- """
101
- pack = Package(
102
- category=category, package=package, request_source=request_source
103
- )
104
- return Mail(sender=sender, recipient=recipient, package=pack)
105
-
106
- def delete_source(self, source_id: IDType) -> None:
107
- """
108
- Remove a source from the manager, discarding any associated mail.
109
-
110
- Parameters
111
- ----------
112
- source_id : IDType
113
- The ID of the source to be removed.
114
-
115
- Raises
116
- ------
117
- ItemNotFoundError
118
- If the given source ID is not present.
119
- """
120
- if source_id not in self.sources:
121
- raise ItemNotFoundError(f"Source {source_id} does not exist.")
122
- self.sources.pop(source_id)
123
- self.mails.pop(source_id)
124
-
125
- def collect(self, sender: IDType) -> None:
126
- """
127
- Collect outbound mail from a single source.
128
-
129
- Parameters
130
- ----------
131
- sender : IDType
132
- The ID of the sender whose outbound mail is retrieved.
133
-
134
- Raises
135
- ------
136
- ItemNotFoundError
137
- If the sender is not recognized.
138
- """
139
- if sender not in self.sources:
140
- raise ItemNotFoundError(f"Sender source {sender} does not exist.")
141
- mailbox: Exchange = (
142
- self.sources[sender]
143
- if isinstance(self.sources[sender], Exchange)
144
- else self.sources[sender].mailbox
145
- )
146
- while mailbox.pending_outs.size() > 0:
147
- mail_id = mailbox.pending_outs.popleft()
148
- mail: Mail = mailbox.pile_.pop(mail_id)
149
- if mail.recipient not in self.sources:
150
- rec_ = mail.recipient
151
- raise ItemNotFoundError(
152
- f"Recipient source {rec_} does not exist"
153
- )
154
- if mail.sender not in self.mails[mail.recipient]:
155
- self.mails[mail.recipient].update({mail.sender: deque()})
156
- self.mails[mail.recipient][mail.sender].append(mail)
157
-
158
- def send(self, recipient: IDType) -> None:
159
- """
160
- Send any pending mail to a specified recipient.
161
-
162
- Parameters
163
- ----------
164
- recipient : IDType
165
- The ID of the recipient to which mail should be delivered.
166
-
167
- Raises
168
- ------
169
- ItemNotFoundError
170
- If the recipient ID is not recognized.
171
- """
172
- if recipient not in self.sources:
173
- raise ItemNotFoundError(
174
- f"Recipient source {recipient} does not exist."
175
- )
176
- if not self.mails[recipient]:
177
- return
178
- for key in list(self.mails[recipient].keys()):
179
- pending_mails = self.mails[recipient].pop(key)
180
- mailbox: Exchange = (
181
- self.sources[recipient]
182
- if isinstance(self.sources[recipient], Exchange)
183
- else self.sources[recipient].mailbox
184
- )
185
- while pending_mails:
186
- mail = pending_mails.popleft()
187
- mailbox.include(mail, direction="in")
188
-
189
- def collect_all(self) -> None:
190
- """
191
- Collect outbound mail from all known sources.
192
- """
193
- for source in self.sources:
194
- self.collect(sender=source.id)
195
-
196
- def send_all(self) -> None:
197
- """
198
- Send mail to all known recipients who have pending items.
199
- """
200
- for source in self.sources:
201
- self.send(recipient=source.id)
202
-
203
- async def execute(self, refresh_time: int = 1) -> None:
204
- """
205
- Continuously collect and send mail in an asynchronous loop.
206
-
207
- Parameters
208
- ----------
209
- refresh_time : int, optional
210
- Delay (in seconds) between each collect/send cycle.
211
- """
212
- while not self.execute_stop:
213
- self.collect_all()
214
- self.send_all()
215
- await asyncio.sleep(refresh_time)
216
-
217
-
218
- # File: lion_core/communication/manager.py
@@ -1,101 +0,0 @@
1
- # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
2
- # SPDX-License-Identifier: Apache-2.0
3
-
4
- from enum import Enum
5
- from typing import Any
6
-
7
- from lionagi.ln import now_utc
8
- from lionagi.protocols.generic.element import ID, IDType
9
-
10
- from .._concepts import Communicatable, Observable
11
-
12
-
13
- class PackageCategory(str, Enum):
14
- """
15
- Enumeration of common package categories in LionAGI:
16
-
17
- - MESSAGE: General message content
18
- - TOOL: A tool or action to be invoked
19
- - IMODEL: Some internal model reference
20
- - NODE: A node in a graph
21
- - NODE_LIST: A list of nodes
22
- - NODE_ID: A node ID
23
- - START: A 'start' signal
24
- - END: An 'end' signal
25
- - CONDITION: A condition or gating logic
26
- - SIGNAL: A more generic signal or marker
27
- """
28
-
29
- MESSAGE = "message"
30
- TOOL = "tool"
31
- IMODEL = "imodel"
32
- NODE = "node"
33
- NODE_LIST = "node_list"
34
- NODE_ID = "node_id"
35
- START = "start"
36
- END = "end"
37
- CONDITION = "condition"
38
- SIGNAL = "signal"
39
-
40
-
41
- def validate_category(value: Any) -> PackageCategory:
42
- """
43
- Validate and convert the input to a valid PackageCategory.
44
-
45
- Parameters
46
- ----------
47
- value : Any
48
- The input to interpret as a `PackageCategory`.
49
-
50
- Returns
51
- -------
52
- PackageCategory
53
- The validated category.
54
-
55
- Raises
56
- ------
57
- ValueError
58
- If the value cannot be converted into a valid package category.
59
- """
60
- if isinstance(value, PackageCategory):
61
- return value
62
- try:
63
- return PackageCategory(str(value))
64
- except ValueError as e:
65
- raise ValueError("Invalid value for category.") from e
66
-
67
-
68
- class Package(Observable):
69
- """
70
- A self-contained package that can be attached to `Mail` items.
71
- Includes a unique ID, creation timestamp, category, payload item,
72
- and an optional request source for context.
73
-
74
- Attributes
75
- ----------
76
- category : PackageCategory
77
- The classification or type of package.
78
- item : Any
79
- The main payload or data of this package.
80
- request_source : ID[Communicatable] | None
81
- An optional reference indicating the origin or context
82
- for this package.
83
- """
84
-
85
- __slots__ = ("id", "created_at", "category", "item", "request_source")
86
-
87
- def __init__(
88
- self,
89
- category: PackageCategory,
90
- item: Any,
91
- request_source: ID[Communicatable] = None,
92
- ):
93
- super().__init__()
94
- self.id = IDType.create()
95
- self.created_at = now_utc().timestamp()
96
- self.category = validate_category(category)
97
- self.item = item
98
- self.request_source = request_source
99
-
100
-
101
- # File: lionagi/protocols/mail/package.py
@@ -1,28 +0,0 @@
1
- ## Rendering Templates
2
-
3
- Once templates are defined, you can load and render them using Jinja2:
4
-
5
- ```python
6
- from jinja2 import Environment, FileSystemLoader
7
-
8
- env = Environment(loader=FileSystemLoader('templates'))
9
-
10
- # Example: Rendering an instruction message
11
- template = env.get_template('instruction_message.jinja2')
12
- args = {
13
- "guidance": "Please ensure accuracy.",
14
- "instruction": "Summarize the document",
15
- "context": ["Doc1: ...", "Doc2: ..."],
16
- "request_fields": {"summary": "...", "key_points": "..."}
17
- }
18
- message_text = template.render(**args)
19
- print(message_text)
20
- ```
21
-
22
- Benefits and Customization
23
- • You can easily rearrange sections within these templates without changing your code logic.
24
- • Each message type is clearly separated, making it simpler to maintain or adjust one message format without affecting the others.
25
- • If you find that you use some snippet (like rendering a schema) in multiple templates, you can factor it out into its own partial template (like tool_schemas.jinja2) and include it where needed.
26
- • Over time, you can add more templates or split existing ones if certain messages become too complex.
27
-
28
- By establishing this set of base templates and arguments, you have a starting point. You can expand or refine as your requirements evolve.
@@ -1,5 +0,0 @@
1
- Please perform the following action:
2
- - Function: {{ function }}
3
- - Arguments:
4
- ```json
5
- {{ arguments | tojson(indent=2) }}
@@ -1,9 +0,0 @@
1
- The action has been performed. Results:
2
-
3
- Function: {{ function }}
4
- Arguments:
5
- ```json
6
- {{ arguments | tojson(indent=2) }}
7
-
8
- Output:
9
- {{ output }}
@@ -1,6 +0,0 @@
1
- # Assistant Response
2
- {% if name is defined %}
3
- **Name** {{ name }}:
4
- {% endif %}
5
- **Response**:
6
- {{ assistant_response }}
@@ -1,61 +0,0 @@
1
- {%- set content = [] -%}
2
-
3
- {# If plain_content is provided, we use it directly. Otherwise, we build a text block from guidance, instruction, context, etc. #}
4
- {% if plain_content %}
5
- {% set content = content + [{"type": "text", "text": plain_content}] %}
6
- {% else %}
7
- {# Build a text block from the available sections #}
8
- {% set text_block = "" %}
9
-
10
- {% if guidance %}
11
- ## Guidance
12
- {{ guidance }}
13
-
14
- {% endif %}
15
-
16
- {% if instruction %}
17
- ## Instruction
18
- {{ instruction }}
19
-
20
- {% endif %}
21
-
22
- {% if context %}
23
- ## Context
24
- {% for item in context %}
25
- - {{ item }}
26
- {% endfor %}
27
- {% endif %}
28
-
29
- {% if tool_schemas %}
30
- ## Tool Schemas
31
- {% include "tool_schemas.jinja2" %}
32
- {% endif %}
33
-
34
- {% if request_fields %}
35
- ## Requested Fields
36
- Please return a JSON object with the following fields:
37
- ```json
38
- {{ request_fields | tojson(indent=2) }}
39
-
40
- {% endif %}
41
-
42
- {% if request_response_format %}
43
-
44
- Response Format
45
-
46
- {{ request_response_format }}
47
- {% endif %}
48
-
49
- {# Append the built sections into text_block #}
50
- {% set text_block = text_block + self %}
51
- {% set content = content + [{"type": "text", "text": text_block.strip()}] %}
52
- {% endif %}
53
-
54
- {# If images are present, we append image_url items to content #}
55
- {% if images %}
56
- {% for img in images %}
57
- {% set content = content + [{"type": "image_url", "image_url": {"url": "data:image/jpeg;base64," ~ img, "detail": image_detail}}] %}
58
- {% endfor %}
59
- {% endif %}
60
-
61
- {{ content | tojson(indent=2) }}
@@ -1,11 +0,0 @@
1
- {{ system_message }}
2
- {% if system_datetime %}
3
- System datetime: {{ system_datetime }}
4
- {% endif %}
5
-
6
- {% if additional_context %}
7
- Additional Context:
8
- {% for c in additional_context %}
9
- - {{ c }}
10
- {% endfor %}
11
- {% endif %}
@@ -1,7 +0,0 @@
1
- {% for name, schema in tool_schemas.items() %}
2
- Tool: {{ name }}
3
- Schema:
4
- ```json
5
- {{ schema | tojson(indent=2) }}
6
-
7
- {% endfor %}
@@ -1,2 +0,0 @@
1
- # Copyright (c) 2023-2025, HaiyangLi <quantocean.li at gmail dot com>
2
- # SPDX-License-Identifier: Apache-2.0
@@ -1,28 +0,0 @@
1
- from .anthropic_ import AnthropicMessagesEndpoint
2
- from .claude_code_cli import ClaudeCodeCLIEndpoint, ClaudeCodeRequest
3
- from .exa_ import ExaSearchEndpoint, ExaSearchRequest
4
- from .oai_ import (
5
- GroqChatEndpoint,
6
- OpenaiChatEndpoint,
7
- OpenaiEmbedEndpoint,
8
- OpenaiResponseEndpoint,
9
- OpenrouterChatEndpoint,
10
- )
11
- from .ollama_ import OllamaChatEndpoint
12
- from .perplexity_ import PerplexityChatEndpoint, PerplexityChatRequest
13
-
14
- __all__ = (
15
- "AnthropicMessagesEndpoint",
16
- "ClaudeCodeRequest",
17
- "ClaudeCodeCLIEndpoint",
18
- "ExaSearchEndpoint",
19
- "ExaSearchRequest",
20
- "OpenaiChatEndpoint",
21
- "OpenaiEmbedEndpoint",
22
- "OpenaiResponseEndpoint",
23
- "OpenrouterChatEndpoint",
24
- "GroqChatEndpoint",
25
- "OllamaChatEndpoint",
26
- "PerplexityChatEndpoint",
27
- "PerplexityChatRequest",
28
- )
@@ -1,198 +0,0 @@
1
- """
2
- OpenAI Model Names extracted from generated models.
3
-
4
- This module provides lists of allowed model names for different OpenAI services,
5
- extracted from the auto-generated openai_models.py file.
6
- """
7
-
8
- from typing import Literal, get_args
9
-
10
- # Manually define the chat models from the ChatModel class in openai_models.py
11
- # These are extracted from the Literal type definition
12
- CHAT_MODELS = [
13
- "gpt-5",
14
- "gpt-5-mini",
15
- "gpt-5-nano",
16
- "gpt-5-2025-08-07",
17
- "gpt-5-mini-2025-08-07",
18
- "gpt-5-nano-2025-08-07",
19
- "gpt-5-chat-latest",
20
- "gpt-4.1",
21
- "gpt-4.1-mini",
22
- "gpt-4.1-nano",
23
- "gpt-4.1-2025-04-14",
24
- "gpt-4.1-mini-2025-04-14",
25
- "gpt-4.1-nano-2025-04-14",
26
- "o4-mini",
27
- "o4-mini-2025-04-16",
28
- "o3",
29
- "o3-2025-04-16",
30
- "o3-mini",
31
- "o3-mini-2025-01-31",
32
- "o1",
33
- "o1-2024-12-17",
34
- "o1-preview",
35
- "o1-preview-2024-09-12",
36
- "o1-mini",
37
- "o1-mini-2024-09-12",
38
- "gpt-4o",
39
- "gpt-4o-2024-11-20",
40
- "gpt-4o-2024-08-06",
41
- "gpt-4o-2024-05-13",
42
- "gpt-4o-audio-preview",
43
- "gpt-4o-audio-preview-2024-10-01",
44
- "gpt-4o-audio-preview-2024-12-17",
45
- "gpt-4o-audio-preview-2025-06-03",
46
- "gpt-4o-mini-audio-preview",
47
- "gpt-4o-mini-audio-preview-2024-12-17",
48
- "gpt-4o-search-preview",
49
- "gpt-4o-mini-search-preview",
50
- "gpt-4o-search-preview-2025-03-11",
51
- "gpt-4o-mini-search-preview-2025-03-11",
52
- "chatgpt-4o-latest",
53
- "codex-mini-latest",
54
- "gpt-4o-mini",
55
- "gpt-4o-mini-2024-07-18",
56
- "gpt-4-turbo",
57
- "gpt-4-turbo-2024-04-09",
58
- "gpt-4-0125-preview",
59
- "gpt-4-turbo-preview",
60
- "gpt-4-1106-preview",
61
- "gpt-4-vision-preview",
62
- "gpt-4",
63
- "gpt-4-0314",
64
- "gpt-4-0613",
65
- "gpt-4-32k",
66
- "gpt-4-32k-0314",
67
- "gpt-4-32k-0613",
68
- "gpt-3.5-turbo",
69
- "gpt-3.5-turbo-16k",
70
- "gpt-3.5-turbo-0301",
71
- "gpt-3.5-turbo-0613",
72
- "gpt-3.5-turbo-1106",
73
- "gpt-3.5-turbo-0125",
74
- "gpt-3.5-turbo-16k-0613",
75
- ]
76
-
77
- # Reasoning models (o1, o3, o4 series)
78
- # Note: Add o1-pro models that may not be in the generated list yet
79
- ADDITIONAL_REASONING_MODELS = [
80
- "o1-pro",
81
- "o1-pro-2025-03-19",
82
- "o3-pro",
83
- "o3-pro-2025-06-10",
84
- ]
85
-
86
- REASONING_MODELS = [
87
- model
88
- for model in CHAT_MODELS
89
- if model.startswith(("o1", "o1-", "o3", "o3-", "o4", "o4-", "gpt-5"))
90
- ] + ADDITIONAL_REASONING_MODELS
91
-
92
- # GPT models (excluding reasoning models)
93
- GPT_MODELS = [
94
- model
95
- for model in CHAT_MODELS
96
- if model.startswith("gpt")
97
- or model.startswith("chatgpt")
98
- or model.startswith("codex")
99
- ]
100
-
101
- # Embedding models
102
- EMBEDDING_MODELS = [
103
- "text-embedding-ada-002",
104
- "text-embedding-3-small",
105
- "text-embedding-3-large",
106
- ]
107
-
108
- # Audio models
109
- AUDIO_MODELS = {
110
- "tts": ["tts-1", "tts-1-hd", "gpt-4o-mini-tts"],
111
- "transcription": [
112
- "whisper-1",
113
- "gpt-4o-transcribe",
114
- "gpt-4o-mini-transcribe",
115
- ],
116
- }
117
-
118
- # Image models
119
- IMAGE_MODELS = ["dall-e-2", "dall-e-3", "gpt-image-1"]
120
-
121
- # Moderation models
122
- MODERATION_MODELS = ["text-moderation-latest", "text-moderation-stable"]
123
-
124
-
125
- def is_reasoning_model(model: str) -> bool:
126
- """Check if a model is a reasoning model (o1/o3/o4 series)."""
127
- return model in REASONING_MODELS
128
-
129
-
130
- def is_valid_chat_model(model: str) -> bool:
131
- """Check if a model is a valid chat model."""
132
- return model in CHAT_MODELS
133
-
134
-
135
- def is_valid_embedding_model(model: str) -> bool:
136
- """Check if a model is a valid embedding model."""
137
- return model in EMBEDDING_MODELS
138
-
139
-
140
- def get_model_category(model: str) -> str:
141
- """Get the category of a model."""
142
- if model in REASONING_MODELS:
143
- return "reasoning"
144
- elif model in GPT_MODELS:
145
- return "gpt"
146
- elif model in EMBEDDING_MODELS:
147
- return "embedding"
148
- elif model in AUDIO_MODELS["tts"]:
149
- return "tts"
150
- elif model in AUDIO_MODELS["transcription"]:
151
- return "transcription"
152
- elif model in IMAGE_MODELS:
153
- return "image"
154
- elif model in MODERATION_MODELS:
155
- return "moderation"
156
- else:
157
- return "unknown"
158
-
159
-
160
- def validate_model(model: str, category: str = None) -> bool:
161
- """
162
- Validate if a model is valid, optionally checking category.
163
-
164
- Args:
165
- model: The model name to validate
166
- category: Optional category to check against ('chat', 'embedding', etc.)
167
-
168
- Returns:
169
- True if model is valid (and in the specified category if provided)
170
- """
171
- if category == "chat":
172
- return model in CHAT_MODELS or model in ADDITIONAL_REASONING_MODELS
173
- elif category == "reasoning":
174
- return is_reasoning_model(model)
175
- elif category == "embedding":
176
- return is_valid_embedding_model(model)
177
- elif category:
178
- return get_model_category(model) == category
179
- else:
180
- # Check if model exists in any category
181
- return get_model_category(model) != "unknown"
182
-
183
-
184
- # Export all model lists
185
- __all__ = [
186
- "CHAT_MODELS",
187
- "REASONING_MODELS",
188
- "GPT_MODELS",
189
- "EMBEDDING_MODELS",
190
- "AUDIO_MODELS",
191
- "IMAGE_MODELS",
192
- "MODERATION_MODELS",
193
- "is_reasoning_model",
194
- "is_valid_chat_model",
195
- "is_valid_embedding_model",
196
- "get_model_category",
197
- "validate_model",
198
- ]