Skip to content

Commit 28e2a40

Browse files
committed
add image url in coze
1 parent 0428d19 commit 28e2a40

File tree

5 files changed

+66
-40
lines changed

5 files changed

+66
-40
lines changed

tests/test_tos.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import pytest
1616
from unittest import mock
17-
import veadk.database.tos.tos_client as tos_mod
17+
import veadk.integrations.ve_tos.ve_tos as tos_mod
1818

1919
# 使用 pytest-asyncio
2020
pytest_plugins = ("pytest_asyncio",)
@@ -47,7 +47,7 @@ def __init__(self, msg):
4747

4848
@pytest.fixture
4949
def tos_client(mock_client):
50-
return tos_mod.TOSClient()
50+
return tos_mod.VeTOS()
5151

5252

5353
def test_create_bucket_exists(tos_client, mock_client):
@@ -75,6 +75,7 @@ async def test_upload_bytes_success(tos_client, mock_client):
7575
result = await tos_client.upload("obj-key", data)
7676
assert result is True
7777
mock_client.put_object.assert_called_once()
78+
mock_client.close.assert_called_once()
7879

7980

8081
@pytest.mark.asyncio
@@ -86,6 +87,7 @@ async def test_upload_file_success(tmp_path, tos_client, mock_client):
8687
result = await tos_client.upload("obj-key", str(file_path))
8788
assert result is True
8889
mock_client.put_object_from_file.assert_called_once()
90+
mock_client.close.assert_called_once()
8991

9092

9193
def test_download_success(tmp_path, tos_client, mock_client):
@@ -101,8 +103,3 @@ def test_download_fail(tos_client, mock_client):
101103
mock_client.get_object.side_effect = Exception("boom")
102104
result = tos_client.download("obj-key", "somewhere.txt")
103105
assert result is False
104-
105-
106-
def test_close(tos_client, mock_client):
107-
tos_client.close()
108-
mock_client.close.assert_called_once()
Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from typing import Union
2121
from pydantic import BaseModel, Field
2222
from typing import Any
23+
from urllib.parse import urlparse
24+
from datetime import datetime
2325

2426
logger = get_logger(__name__)
2527

@@ -43,7 +45,7 @@ class TOSConfig(BaseModel):
4345
)
4446

4547

46-
class TOSClient(BaseModel):
48+
class VeTOS(BaseModel):
4749
config: TOSConfig = Field(default_factory=TOSConfig)
4850

4951
def model_post_init(self, __context: Any) -> None:
@@ -78,6 +80,23 @@ def create_bucket(self) -> bool:
7880
logger.error(f"Bucket creation failed: {str(e)}")
7981
return False
8082

83+
def build_tos_url(
84+
self, user_id: str, app_name: str, session_id: str, data_path: str
85+
) -> tuple[str, str]:
86+
"""generate TOS object key"""
87+
parsed_url = urlparse(data_path)
88+
89+
if parsed_url.scheme and parsed_url.scheme in ("http", "https", "ftp", "ftps"):
90+
file_name = os.path.basename(parsed_url.path)
91+
else:
92+
file_name = os.path.basename(data_path)
93+
94+
timestamp: str = datetime.now().strftime("%Y%m%d%H%M%S%f")[:-3]
95+
object_key: str = f"{app_name}-{user_id}-{session_id}/{timestamp}-{file_name}"
96+
tos_url: str = f"https://{self.config.bucket_name}.tos-{self.config.region}.volces.com/{object_key}"
97+
98+
return object_key, tos_url
99+
81100
def upload(
82101
self,
83102
object_key: str,
@@ -106,9 +125,11 @@ def _do_upload_bytes(self, object_key: str, bytes: bytes) -> bool:
106125
bucket=self.config.bucket_name, key=object_key, content=bytes
107126
)
108127
logger.debug(f"Upload success, object_key: {object_key}")
128+
self._close()
109129
return True
110130
except Exception as e:
111131
logger.error(f"Upload failed: {e}")
132+
self._close()
112133
return False
113134

114135
def _do_upload_file(self, object_key: str, file_path: str) -> bool:
@@ -121,10 +142,12 @@ def _do_upload_file(self, object_key: str, file_path: str) -> bool:
121142
self._client.put_object_from_file(
122143
bucket=self.config.bucket_name, key=object_key, file_path=file_path
123144
)
145+
self._close()
124146
logger.debug(f"Upload success, object_key: {object_key}")
125147
return True
126148
except Exception as e:
127149
logger.error(f"Upload failed: {e}")
150+
self._close()
128151
return False
129152

130153
def download(self, object_key: str, save_path: str) -> bool:
@@ -148,6 +171,6 @@ def download(self, object_key: str, save_path: str) -> bool:
148171

149172
return False
150173

151-
def close(self):
174+
def _close(self):
152175
if self._client:
153176
self._client.close()

veadk/runner.py

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,8 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import os
1514
import asyncio
1615
from typing import Union
17-
from urllib.parse import urlparse
18-
from datetime import datetime
1916

2017
from google.adk.agents import RunConfig
2118
from google.adk.agents.invocation_context import LlmCallsLimitExceededError
@@ -35,7 +32,7 @@
3532
from veadk.types import MediaMessage
3633
from veadk.utils.logger import get_logger
3734
from veadk.utils.misc import read_png_to_bytes
38-
from veadk.database.tos.tos_client import TOSClient
35+
from veadk.integrations.ve_tos.ve_tos import VeTOS
3936

4037
logger = get_logger(__name__)
4138

@@ -89,27 +86,6 @@ def __init__(
8986
plugins=plugins,
9087
)
9188

92-
def _build_tos_object_key(
93-
self, user_id: str, app_name: str, session_id: str, data_path: str
94-
) -> str:
95-
"""generate TOS object key"""
96-
parsed_url = urlparse(data_path)
97-
98-
if parsed_url.scheme and parsed_url.scheme in ("http", "https", "ftp", "ftps"):
99-
file_name = os.path.basename(parsed_url.path)
100-
else:
101-
file_name = os.path.basename(data_path)
102-
103-
timestamp: str = datetime.now().strftime("%Y%m%d%H%M%S%f")[:-3]
104-
object_key: str = f"{app_name}-{user_id}-{session_id}/{timestamp}-{file_name}"
105-
return object_key
106-
107-
def _upload_to_tos(self, data: Union[str, bytes], object_key: str):
108-
tos_client = TOSClient()
109-
asyncio.create_task(tos_client.upload(object_key, data))
110-
tos_client.close()
111-
return
112-
11389
def _convert_messages(self, messages, session_id) -> list:
11490
if isinstance(messages, str):
11591
messages = [types.Content(role="user", parts=[types.Part(text=messages)])]
@@ -118,14 +94,16 @@ def _convert_messages(self, messages, session_id) -> list:
11894
"The MediaMessage only supports PNG format file for now."
11995
)
12096
data = read_png_to_bytes(messages.media)
97+
98+
ve_tos = VeTOS()
99+
object_key, tos_url = ve_tos.build_tos_url(
100+
self.user_id, self.app_name, session_id, messages.media
101+
)
121102
try:
122-
object_key = self._build_tos_object_key(
123-
self.user_id, self.app_name, session_id, messages.media
124-
)
125-
self._upload_to_tos(data, object_key)
103+
asyncio.create_task(ve_tos.upload(object_key, data))
126104
except Exception as e:
127105
logger.error(f"Upload to TOS failed: {e}")
128-
object_key = None
106+
tos_url = None
129107

130108
messages = [
131109
types.Content(
@@ -134,7 +112,7 @@ def _convert_messages(self, messages, session_id) -> list:
134112
types.Part(text=messages.text),
135113
types.Part(
136114
inline_data=Blob(
137-
display_name=object_key,
115+
display_name=tos_url,
138116
data=data,
139117
mime_type="image/png",
140118
)

veadk/tracing/telemetry/attributes/extractors/llm_attributes_extractors.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,15 @@ def llm_gen_ai_prompt(params: LLMAttributesParams) -> ExtractorResponse:
137137
if part.function_call.args
138138
else json.dumps({})
139139
)
140+
# image
141+
if part.inline_data:
142+
message[f"gen_ai.prompt.{idx}.type"] = "image_url"
143+
message[f"gen_ai.prompt.{idx}.image_url.name"] = (
144+
part.inline_data.display_name.split("/")[-1]
145+
)
146+
message[f"gen_ai.prompt.{idx}.image_url.url"] = (
147+
part.inline_data.display_name
148+
)
140149

141150
if message:
142151
messages.append(message)
@@ -234,6 +243,14 @@ def llm_gen_ai_user_message(params: LLMAttributesParams) -> ExtractorResponse:
234243
message_part[f"parts.{idx}.content"] = str(
235244
part.function_response
236245
)
246+
if part.inline_data:
247+
message_part[f"parts.{idx}.type"] = "image_url"
248+
message_part[f"parts.{idx}.image_url.name"] = (
249+
part.inline_data.display_name.split("/")[-1]
250+
)
251+
message_part[f"parts.{idx}.image_url.url"] = (
252+
part.inline_data.display_name
253+
)
237254

238255
message_parts.append(message_part)
239256

veadk/tracing/telemetry/telemetry.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ def _set_agent_input_attribute(
8787
"gen_ai.user.message",
8888
{f"parts.{idx}.type": "text", f"parts.{idx}.content": part.text},
8989
)
90+
if part.inline_data:
91+
span.add_event(
92+
"gen_ai.user.message",
93+
{
94+
f"parts.{idx}.type": "image_url",
95+
f"parts.{idx}.image_url.name": part.inline_data.display_name.split(
96+
"/"
97+
)[-1],
98+
f"parts.{idx}.image_url.url": part.inline_data.display_name,
99+
},
100+
)
90101

91102

92103
def _set_agent_output_attribute(span: _Span, llm_response: LlmResponse) -> None:

0 commit comments

Comments
 (0)