auto-coder 0.1.309__py3-none-any.whl → 0.1.310__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.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.309
3
+ Version: 0.1.310
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
14
14
  autocoder/lang.py,sha256=U6AjVV8Rs1uLyjFCZ8sT6WWuNUxMBqkXXIOs4S120uk,14511
15
15
  autocoder/models.py,sha256=AyoZ-Pzy0oyYUmWCxOIRiOImsqboSfRET7LO9-UOuxI,11172
16
16
  autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
17
- autocoder/version.py,sha256=pYQUD_mrZAZNxc9Lw3mOsln_cgf30rD3GGdAewKlpVU,23
17
+ autocoder/version.py,sha256=Yva2ub3_rI3hSMIe4yqnO-D1-Taf21vJw07BOhnUd5E,23
18
18
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  autocoder/agent/auto_demand_organizer.py,sha256=URAq0gSEiHeV_W4zwhOI_83kHz0Ryfj1gcfh5jwCv_w,6501
20
20
  autocoder/agent/auto_filegroup.py,sha256=pBsAkBcpFTff-9L5OwI8xhf2xPKpl-aZwz-skF2B6dc,6296
@@ -74,6 +74,7 @@ autocoder/common/mcp_server.py,sha256=1SCtpBRmN299xWX-0aV0imWS2CX6zBUOZBocbV_J6B
74
74
  autocoder/common/mcp_tools.py,sha256=YdEhDzRnwAr2J3D-23ExIQFWbrNO-EUpIxg179qs9Sw,12666
75
75
  autocoder/common/memory_manager.py,sha256=Xx6Yv0ULxVfcFfmD36hdHFFhxCgRAs-5fTd0fLHJrpQ,3773
76
76
  autocoder/common/model_speed_test.py,sha256=U48xUUpOnbwUal1cdij4YAn_H2PD2pNaqrMHaYtQRfI,15200
77
+ autocoder/common/openai_content.py,sha256=M_V_UyHrqNVWjgrYvxfAupZw2I0Nr3iilYv6SxSvfLA,8091
77
78
  autocoder/common/printer.py,sha256=P1WU0QjlfnjqTP5uA55GkHZCpFzRPFkc34DMMandreg,2023
78
79
  autocoder/common/recall_validation.py,sha256=Avt9Q9dX3kG6Pf2zsdlOHmsjd-OeSj7U1PFBDp_Cve0,1700
79
80
  autocoder/common/result_manager.py,sha256=nBcFRj5reBC7vp13M91f4B8iPW8B8OehayHlUdeAt1g,3776
@@ -137,7 +138,7 @@ autocoder/rag/doc_filter.py,sha256=UduVO2mlrngwJICrefjDJTYfdmQ4GcRXrfWDQ7xXksk,1
137
138
  autocoder/rag/document_retriever.py,sha256=5BDqKVJqLPScEnua5S5suXhWuCaALIfPf5obXeJoWfs,8461
138
139
  autocoder/rag/lang.py,sha256=_jmUtxZDG1fmF4b2mhMJbYS1YQDb2ZE8nyAn5_vrvjA,3350
139
140
  autocoder/rag/llm_wrapper.py,sha256=Ht5GF5yJtrztoliujsZzx_ooWZmHkd5xLZKcGEiicZw,4303
140
- autocoder/rag/long_context_rag.py,sha256=6rqq0pvYe9N4TvyLwd2OB21ZUrPC4FfxZuks0weAz4A,41935
141
+ autocoder/rag/long_context_rag.py,sha256=RE4xse3XxSC_HQA5erqrx6MhanP_29mBRdYOTJQZYGc,42106
141
142
  autocoder/rag/qa_conversation_strategy.py,sha256=1AcHV0MU00yTls20LlCPO-Un_OhSrr_p-H5lxLleAq4,6060
142
143
  autocoder/rag/rag_config.py,sha256=8LwFcTd8OJWWwi1_WY4IzjqgtT6RyE2j4PjxS5cCTDE,802
143
144
  autocoder/rag/rag_entry.py,sha256=6TKtErZ0Us9XSV6HgRKXA6yR3SiZGPHpynOKSaR1wgE,2463
@@ -198,9 +199,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
198
199
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
199
200
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=xuBeWD0YOckqRo8JB1WkVIMOYH6c24m7JfV4svBfPDo,15113
200
201
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
201
- auto_coder-0.1.309.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
202
- auto_coder-0.1.309.dist-info/METADATA,sha256=PK_1Bf18b0XqyZNBitO_y_KFkBlR2sdlwDwYuJ2HjJM,2747
203
- auto_coder-0.1.309.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
204
- auto_coder-0.1.309.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
205
- auto_coder-0.1.309.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
206
- auto_coder-0.1.309.dist-info/RECORD,,
202
+ auto_coder-0.1.310.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
203
+ auto_coder-0.1.310.dist-info/METADATA,sha256=zh8Gtsl5ahulVrleWS6UchHXOiJfeG-8SFeikpSffSg,2747
204
+ auto_coder-0.1.310.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
205
+ auto_coder-0.1.310.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
206
+ auto_coder-0.1.310.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
207
+ auto_coder-0.1.310.dist-info/RECORD,,
@@ -0,0 +1,256 @@
1
+ from typing import Any, Dict, List, Optional, Union
2
+ import base64
3
+ import os
4
+ from enum import Enum
5
+ from pydantic import BaseModel, Field, validator
6
+
7
+
8
+ class ContentType(str, Enum):
9
+ """Type of content in the OpenAI chat message."""
10
+ TEXT = "text"
11
+ IMAGE_URL = "image_url"
12
+
13
+
14
+ class ImageUrl(BaseModel):
15
+ """Image URL structure in OpenAI chat messages."""
16
+ url: str = Field(..., description="URL of the image, can be http(s) or data URI")
17
+
18
+ @validator('url')
19
+ def validate_url(cls, v):
20
+ """Validate that URL is either an http(s) URL or a valid data URI."""
21
+ if v.startswith(('http://', 'https://')):
22
+ return v
23
+ elif v.startswith('data:image/'):
24
+ return v
25
+ else:
26
+ raise ValueError("Image URL must be http(s) or data URI format")
27
+
28
+
29
+ class TextContent(BaseModel):
30
+ """Text content in OpenAI chat messages."""
31
+ type: str = ContentType.TEXT
32
+ text: str
33
+
34
+
35
+ class ImageUrlContent(BaseModel):
36
+ """Image URL content in OpenAI chat messages."""
37
+ type: str = ContentType.IMAGE_URL
38
+ image_url: Union[str, ImageUrl]
39
+
40
+ @validator('image_url')
41
+ def validate_image_url(cls, v):
42
+ """Convert string to ImageUrl if necessary."""
43
+ if isinstance(v, str):
44
+ return ImageUrl(url=v)
45
+ return v
46
+
47
+
48
+ ContentItem = Union[TextContent, ImageUrlContent]
49
+
50
+
51
+ class OpenAIMessage(BaseModel):
52
+ """Model for an OpenAI chat message."""
53
+ role: str
54
+ content: Union[str, List[ContentItem]]
55
+ name: Optional[str] = None
56
+
57
+
58
+ class OpenAIConversation(BaseModel):
59
+ """Model for a conversation with OpenAI."""
60
+ messages: List[OpenAIMessage]
61
+
62
+
63
+ def is_structured_content(content: Any) -> bool:
64
+ """
65
+ Check if the content is structured (list of items with type field).
66
+
67
+ Args:
68
+ content: The content to check
69
+
70
+ Returns:
71
+ bool: True if the content is structured, False otherwise
72
+ """
73
+ if not isinstance(content, list):
74
+ return False
75
+
76
+ if not content:
77
+ return False
78
+
79
+ # Check if all items have a 'type' field
80
+ return all(isinstance(item, dict) and 'type' in item for item in content)
81
+
82
+
83
+ def encode_image_to_base64(image_path: str) -> str:
84
+ """
85
+ Encode an image file to base64.
86
+
87
+ Args:
88
+ image_path: Path to the image file
89
+
90
+ Returns:
91
+ str: Base64-encoded image data
92
+ """
93
+ if not os.path.exists(image_path):
94
+ raise FileNotFoundError(f"Image file not found: {image_path}")
95
+
96
+ with open(image_path, "rb") as image_file:
97
+ encoded_string = base64.b64encode(image_file.read()).decode('utf-8')
98
+
99
+ # Determine content type based on file extension
100
+ file_ext = os.path.splitext(image_path)[1].lower()
101
+ content_type = {
102
+ '.jpg': 'image/jpeg',
103
+ '.jpeg': 'image/jpeg',
104
+ '.png': 'image/png',
105
+ '.gif': 'image/gif',
106
+ '.webp': 'image/webp',
107
+ }.get(file_ext, 'image/jpeg')
108
+
109
+ return f"data:{content_type};base64,{encoded_string}"
110
+
111
+
112
+ def create_text_content(text: str) -> TextContent:
113
+ """
114
+ Create a text content item.
115
+
116
+ Args:
117
+ text: The text content
118
+
119
+ Returns:
120
+ TextContent: A text content item
121
+ """
122
+ return TextContent(text=text)
123
+
124
+
125
+ def create_image_content(image_path_or_url: str) -> ImageUrlContent:
126
+ """
127
+ Create an image content item from a file path or URL.
128
+
129
+ Args:
130
+ image_path_or_url: Path to the image file or an image URL
131
+
132
+ Returns:
133
+ ImageUrlContent: An image content item
134
+ """
135
+ # If it's a URL already, use it directly
136
+ if image_path_or_url.startswith(('http://', 'https://', 'data:')):
137
+ return ImageUrlContent(image_url=image_path_or_url)
138
+
139
+ # Otherwise, treat it as a file path and encode it
140
+ return ImageUrlContent(image_url=encode_image_to_base64(image_path_or_url))
141
+
142
+
143
+ def normalize_content(content: Any) -> Union[str, List[ContentItem]]:
144
+ """
145
+ Normalize content to either a string or a list of structured content items.
146
+
147
+ Args:
148
+ content: The content to normalize
149
+
150
+ Returns:
151
+ Union[str, List[ContentItem]]: Normalized content
152
+ """
153
+ if isinstance(content, str):
154
+ return content
155
+
156
+ if is_structured_content(content):
157
+ normalized_items = []
158
+ for item in content:
159
+ if item['type'] == ContentType.TEXT:
160
+ normalized_items.append(create_text_content(item['text']))
161
+ elif item['type'] == ContentType.IMAGE_URL:
162
+ normalized_items.append(ImageUrlContent(image_url=item['image_url']))
163
+ return normalized_items
164
+
165
+ # If it's neither a string nor structured content, convert to string
166
+ return str(content)
167
+
168
+
169
+ def create_message(role: str, content: Union[str, List[ContentItem]], name: Optional[str] = None) -> OpenAIMessage:
170
+ """
171
+ Create an OpenAI chat message.
172
+
173
+ Args:
174
+ role: Role of the message sender (system, user, assistant)
175
+ content: Content of the message (string or structured content)
176
+ name: Optional name of the sender
177
+
178
+ Returns:
179
+ OpenAIMessage: An OpenAI chat message
180
+ """
181
+ return OpenAIMessage(
182
+ role=role,
183
+ content=normalize_content(content),
184
+ name=name
185
+ )
186
+
187
+
188
+ def process_conversations(conversations: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
189
+ """
190
+ 处理会话列表,确保每个消息都符合标准格式,并只保留文本内容。
191
+
192
+ Args:
193
+ conversations: 会话列表,可能包含各种格式的消息
194
+
195
+ Returns:
196
+ List[Dict[str, Any]]: 标准化后的会话列表,每个消息都有 role 和 content 字段
197
+
198
+ 例子:
199
+ >>> conversations = [
200
+ ... {"role": "user", "content": "Hello"},
201
+ ... {"role": "assistant", "content": "Hi, how can I help?"},
202
+ ... {"role": "user", "content": [
203
+ ... {"type": "text", "text": "What's in this image?"},
204
+ ... {"type": "image_url", "image_url": "data:image/jpeg;base64,/9j/4AAQ..."}
205
+ ... ]}
206
+ ... ]
207
+ >>> processed = process_conversations(conversations)
208
+ >>> # 结果保持相同的结构,但确保格式一致性
209
+
210
+ 输出格式要是这样的:
211
+ [
212
+ {"role": "user", "content": "Hello"},
213
+ {"role": "assistant", "content": "Hi, how can I help?"},
214
+ {"role": "user", "content": "What's in this image?"}
215
+ ]
216
+
217
+ 只保留 text 内容。如果有多个 text 内容,用换行符连接弄成一个。
218
+
219
+ """
220
+ processed_conversations = []
221
+
222
+ for message in conversations:
223
+ # 确保消息有 role 字段
224
+ if "role" not in message:
225
+ raise ValueError(f"Message missing 'role' field: {message}")
226
+
227
+ role = message["role"]
228
+
229
+ # 处理 content 字段,确保存在
230
+ if "content" not in message:
231
+ processed_content = "" # 如果不存在,设置为空字符串
232
+ else:
233
+ content = message["content"]
234
+
235
+ # 处理结构化内容
236
+ if isinstance(content, list) and is_structured_content(content):
237
+ # 提取所有文本内容并用换行符连接
238
+ text_contents = []
239
+ for item in content:
240
+ if item.get('type') == ContentType.TEXT and 'text' in item:
241
+ text_contents.append(item['text'])
242
+ processed_content = '\n'.join(text_contents)
243
+ else:
244
+ # 如果是字符串或其他类型,确保转换为字符串
245
+ processed_content = str(content) if content is not None else ""
246
+
247
+ # 构建标准化的消息
248
+ processed_message = {"role": role, "content": processed_content}
249
+
250
+ # 如果原消息有 name 字段,也加入
251
+ if "name" in message and message["name"]:
252
+ processed_message["name"] = message["name"]
253
+
254
+ processed_conversations.append(processed_message)
255
+
256
+ return processed_conversations
@@ -40,6 +40,7 @@ from autocoder.rag.lang import get_message_with_format_and_newline
40
40
  from autocoder.rag.qa_conversation_strategy import get_qa_strategy
41
41
  from autocoder.rag.searchable import SearchableResults
42
42
  from autocoder.rag.conversation_to_queries import extract_search_queries
43
+ from autocoder.common import openai_content as OpenAIContentProcessor
43
44
  try:
44
45
  from autocoder_pro.rag.llm_compute import LLMComputeEngine
45
46
  pro_version = version("auto-coder-pro")
@@ -348,7 +349,7 @@ class LongContextRAG:
348
349
  role_mapping=None,
349
350
  llm_config: Dict[str, Any] = {},
350
351
  extra_request_params: Dict[str, Any] = {}
351
- ):
352
+ ):
352
353
  try:
353
354
  return self._stream_chat_oai(
354
355
  conversations,
@@ -399,6 +400,7 @@ class LongContextRAG:
399
400
  llm_config: Dict[str, Any] = {},
400
401
  extra_request_params: Dict[str, Any] = {}
401
402
  ):
403
+ conversations = OpenAIContentProcessor.process_conversations(conversations)
402
404
  if self.client:
403
405
  model = model or self.args.model
404
406
  response = self.client.chat.completions.create(
@@ -415,6 +417,7 @@ class LongContextRAG:
415
417
  target_llm = self.llm.get_sub_client("qa_model")
416
418
 
417
419
  query = conversations[-1]["content"]
420
+
418
421
  context = []
419
422
 
420
423
  if (
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.309"
1
+ __version__ = "0.1.310"