From fa905b59c89ecbc046427f68e66e0f029595b289 Mon Sep 17 00:00:00 2001 From: C9luster <138663536+C9luster@users.noreply.github.com> Date: Thu, 18 Jul 2024 11:17:20 +0800 Subject: [PATCH] =?UTF-8?q?Appbuilder-sdk=E7=9A=84Trace=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E3=80=90=E5=AE=9E=E7=8E=B0Components&Assistants=20API=E7=9A=84?= =?UTF-8?q?Trace=E3=80=91=20(#407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * trace-for-assistant-components * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update * update --------- Co-authored-by: yinjiaqi --- appbuilder/__init__.py | 5 +- .../core/assistant/assistants/assistants.py | 11 +- appbuilder/core/assistant/assistants/files.py | 12 +- .../assistant/threads/messages/messages.py | 8 +- .../core/assistant/threads/runs/runs.py | 12 +- .../core/assistant/threads/runs/steps.py | 3 + appbuilder/core/assistant/threads/threads.py | 6 +- .../components/animal_recognize/component.py | 3 + appbuilder/core/components/asr/component.py | 3 + .../components/dish_recognize/component.py | 2 + .../components/doc_crop_enhance/component.py | 2 + .../doc_format_converter/component.py | 3 + .../core/components/doc_parser/doc_parser.py | 2 + .../components/doc_splitter/doc_splitter.py | 4 + .../core/components/embeddings/component.py | 2 + .../core/components/excel2figure/component.py | 3 + .../components/extract_table/component.py | 2 + .../core/components/gbi/nl2sql/component.py | 2 + .../components/gbi/select_table/component.py | 3 +- .../core/components/general_ocr/component.py | 3 + .../components/handwrite_ocr/component.py | 3 + .../components/image_understand/component.py | 3 + .../landmark_recognize/component.py | 2 + .../llms/dialog_summary/component.py | 2 + .../llms/hallucination_detection/component.py | 3 + .../llms/is_complex_query/component.py | 2 + .../core/components/llms/mrc/component.py | 2 + .../components/llms/nl2pandas/component.py | 2 + .../llms/oral_query_generation/component.py | 3 + .../components/llms/playground/component.py | 2 + .../llms/qa_pair_mining/component.py | 2 + .../llms/query_decomposition/component.py | 2 + .../llms/query_rewrite/component.py | 2 + .../llms/similar_question/component.py | 3 + .../llms/style_rewrite/component.py | 3 + .../llms/style_writing/component.py | 3 + .../llms/tag_extraction/component.py | 2 + .../core/components/matching/component.py | 2 + .../core/components/mix_card_ocr/component.py | 3 + .../components/object_recognize/component.py | 4 +- .../components/plant_recognize/component.py | 3 + .../core/components/qrcode_ocr/component.py | 3 + .../rag_with_baidu_search/component.py | 2 + .../rag_with_baidu_search_pro/component.py | 2 + .../retriever/baidu_vdb/baiduvdb_retriever.py | 2 + .../components/retriever/bes/bes_retriever.py | 2 + .../core/components/table_ocr/component.py | 3 + .../components/text_to_image/component.py | 2 + .../core/components/translate/component.py | 3 + appbuilder/core/components/tts/component.py | 2 + .../tests/test_appbuilder_assistant_trace.py | 82 +++ ...ace.py => test_appbuilder_client_trace.py} | 67 ++- .../tests/test_appbuilder_components_trace.py | 111 ++++ ...test_assistant_e2e_stream_event_handler.py | 33 +- appbuilder/utils/model_util.py | 2 + appbuilder/utils/trace/_function.py | 520 +++++++++++++++++- appbuilder/utils/trace/phoenix_wrapper.py | 25 +- appbuilder/utils/trace/tracer.py | 207 ++++++- appbuilder/utils/trace/tracer_wrapper.py | 172 ++++++ cookbooks/README.md | 3 + .../{trace_client.ipynb => trace.ipynb} | 2 +- docs/README.md | 3 + docs/trace/README.md | 6 + .../trace/README.md => docs/trace/basic.md | 8 +- docs/trace/phoenix_method.md | 33 ++ requirements.txt | 2 +- 66 files changed, 1363 insertions(+), 78 deletions(-) create mode 100644 appbuilder/tests/test_appbuilder_assistant_trace.py rename appbuilder/tests/{test_appbuilder_trace.py => test_appbuilder_client_trace.py} (64%) create mode 100644 appbuilder/tests/test_appbuilder_components_trace.py rename cookbooks/appbuilder_trace/{trace_client.ipynb => trace.ipynb} (99%) create mode 100644 docs/trace/README.md rename appbuilder/utils/trace/README.md => docs/trace/basic.md (73%) create mode 100644 docs/trace/phoenix_method.md diff --git a/appbuilder/__init__.py b/appbuilder/__init__.py index 40ffcbc45..f38bb5326 100644 --- a/appbuilder/__init__.py +++ b/appbuilder/__init__.py @@ -139,7 +139,7 @@ def get_default_header(): from appbuilder.core.assistant.threads.runs import AssistantEventHandler from appbuilder.core.assistant.threads.runs import AssistantStreamManager -from appbuilder.utils.trace.tracer import AppBuilderTracer +from appbuilder.utils.trace.tracer import AppBuilderTracer, AppbuilderInstrumentor __all__ = [ 'logger', @@ -212,5 +212,6 @@ def get_default_header(): "AssistantEventHandler", "AssistantStreamManager", - "AppBuilderTracer" + "AppBuilderTracer", + "AppbuilderInstrumentor" ] diff --git a/appbuilder/core/assistant/assistants/assistants.py b/appbuilder/core/assistant/assistants/assistants.py index 9aa6c4606..e70efc44b 100644 --- a/appbuilder/core/assistant/assistants/assistants.py +++ b/appbuilder/core/assistant/assistants/assistants.py @@ -18,6 +18,7 @@ from appbuilder.utils.collector import AssistantKeys from appbuilder.core._client import AssistantHTTPClient from appbuilder.core.assistant.assistants.files import Files +from appbuilder.utils.trace.tracer_wrapper import assistent_tool_trace class Assistants(object): @@ -28,7 +29,7 @@ def __init__(self): def files(self): return Files() - + @assistent_tool_trace def create(self, name: str, description: str, @@ -91,6 +92,7 @@ def create(self, Collector().add_to_collection(AssistantKeys.ASSISTANT, resp, resp.id) return resp + @assistent_tool_trace def update(self, assistant_id: str, model: Optional[str], @@ -157,6 +159,7 @@ def update(self, resp = assistant_type.AssistantUpdateResponse(**data) return resp + @assistent_tool_trace def list(self, limit: Optional[int] = 20, order: Optional[str] = "desc", @@ -203,6 +206,7 @@ def list(self, resp = assistant_type.AssistantListResponse(**data) return resp + @assistent_tool_trace def query(self, assistant_id: Optional[str]) -> assistant_type.AssistantQueryResponse: """ @@ -240,6 +244,7 @@ def query(self, resp = assistant_type.AssistantQueryResponse(**data) return resp + @assistent_tool_trace def delete(self, assistant_id: Optional[str]) -> assistant_type.AssistantDeleteResponse: """ @@ -277,7 +282,7 @@ def delete(self, resp = assistant_type.AssistantDeleteResponse(**data) return resp - + @assistent_tool_trace def mount_files(self, assistant_id: Optional[str], file_id: Optional[str] @@ -328,6 +333,7 @@ def mount_files(self, resp = assistant_type.AssistantFilesResponse(**data) return resp + @assistent_tool_trace def mounted_files_list(self, assistant_id: Optional[str], limit: Optional[int] = 20, @@ -374,6 +380,7 @@ def mounted_files_list(self, resp = assistant_type.AssistantMountedFilesListResponse(**data) return resp + @assistent_tool_trace def unmount_files(self, assistant_id: Optional[str], file_id: Optional[str] diff --git a/appbuilder/core/assistant/assistants/files.py b/appbuilder/core/assistant/assistants/files.py index c089f1009..69795ab1a 100644 --- a/appbuilder/core/assistant/assistants/files.py +++ b/appbuilder/core/assistant/assistants/files.py @@ -22,11 +22,13 @@ from appbuilder.core._client import AssistantHTTPClient from appbuilder.core._exception import AppBuilderServerException,HTTPConnectionException +from appbuilder.utils.trace.tracer_wrapper import assistent_tool_trace class Files(object): def __init__(self): self._http_client = AssistantHTTPClient() + @assistent_tool_trace def create(self, file_path: str, purpose: str = "assistant") -> assistant_type.AssistantFilesCreateResponse: """ 上传文件到助理存储中。 @@ -71,7 +73,7 @@ def create(self, file_path: str, purpose: str = "assistant") -> assistant_type.A resp = assistant_type.AssistantFilesCreateResponse(**data) return resp - + @assistent_tool_trace def list(self) -> assistant_type.AssistantFilesListResponse: """ 列出存储中的文件列表 @@ -105,7 +107,7 @@ def list(self) -> assistant_type.AssistantFilesListResponse: return resp - + @assistent_tool_trace def query(self, file_id: str, ) -> assistant_type.AssistantFilesQueryResponse: @@ -150,6 +152,7 @@ def query(self, raise ValueError('file_id {} is not exist'.format(file_id)) return resp + @assistent_tool_trace def delete(self, file_id: str, ) -> assistant_type.AssistantFilesDeleteResponse: @@ -180,6 +183,7 @@ def delete(self, resp = assistant_type.AssistantFilesDeleteResponse(**data) return resp + @assistent_tool_trace def download(self, file_id:str, file_path:str="", # 要求若文件路径不为空,需要以/结尾,默认下载到当前文件夹 @@ -245,9 +249,7 @@ def download(self, except Exception as e: raise Exception("出现错误,错误信息{}".format(e)) - - - + @assistent_tool_trace def content(self, file_id:str, timeout:Optional[int]=None): diff --git a/appbuilder/core/assistant/threads/messages/messages.py b/appbuilder/core/assistant/threads/messages/messages.py index de32240f7..72fb3a75a 100644 --- a/appbuilder/core/assistant/threads/messages/messages.py +++ b/appbuilder/core/assistant/threads/messages/messages.py @@ -16,12 +16,14 @@ from appbuilder.core.assistant.type import thread_type from appbuilder.core._client import AssistantHTTPClient from typing import Optional +from appbuilder.utils.trace.tracer_wrapper import assistent_tool_trace class Messages(object): def __init__(self): self._http_client = AssistantHTTPClient() + @assistent_tool_trace def create(self, thread_id: str, content: str, @@ -67,6 +69,7 @@ def create(self, response = thread_type.AssistantMessageCreateResponse(**data) return response + @assistent_tool_trace def update(self, thread_id: str, message_id: str, @@ -107,6 +110,7 @@ def update(self, response = thread_type.AssistantMessageUpdateResponse(**data) return response + @assistent_tool_trace def list(self, thread_id: str, limit: int = 20, @@ -152,7 +156,7 @@ def list(self, response = thread_type.AssistantMessageListResponse(**data) return response - + @assistent_tool_trace def query(self, thread_id:str, message_id:str) -> thread_type.AssistantMessageQueryResponse: @@ -189,7 +193,7 @@ def query(self, response = thread_type.AssistantMessageQueryResponse(**data) return response - + @assistent_tool_trace def files(self, thread_id:str, message_id:str, diff --git a/appbuilder/core/assistant/threads/runs/runs.py b/appbuilder/core/assistant/threads/runs/runs.py index b1c437632..99d03bdaf 100644 --- a/appbuilder/core/assistant/threads/runs/runs.py +++ b/appbuilder/core/assistant/threads/runs/runs.py @@ -22,6 +22,7 @@ from appbuilder.core.assistant.type import public_type from appbuilder.core._client import AssistantHTTPClient from appbuilder.utils.sse_util import SSEClient +from appbuilder.utils.trace.tracer_wrapper import assistent_tool_trace, assistant_run_trace, assistent_stream_run_trace, assistent_stream_run_with_handler_trace @@ -32,7 +33,8 @@ def __init__(self) -> None: @property def steps(self) -> Steps: return Steps() - + + @assistant_run_trace def run(self, assistant_id: str, thread_id: Optional[str] = "", @@ -234,9 +236,9 @@ def _stream(self, stream=True, timeout=None ) - return response + @assistent_stream_run_trace def stream_run(self, assistant_id: str, thread_id: Optional[str] = "", @@ -302,6 +304,7 @@ def stream_run(self, sse_client = SSEClient(response) return self._iterate_events(sse_client.events()) + @assistent_stream_run_with_handler_trace def stream_run_with_handler(self, assistant_id: str, thread_id: Optional[str] = "", @@ -375,6 +378,8 @@ def _iterate_events(self, events): yield result + + @assistent_tool_trace def submit_tool_outputs(self, run_id: str, thread_id: str, @@ -416,6 +421,7 @@ def submit_tool_outputs(self, resp = thread_type.RunResult(**data) return resp + @assistent_tool_trace def cancel(self, run_id: str, thread_id: str) -> thread_type.RunResult: """ 取消指定线程的运行 @@ -451,6 +457,7 @@ def cancel(self, run_id: str, thread_id: str) -> thread_type.RunResult: resp = thread_type.RunResult(**data) return resp + @assistent_tool_trace def list(self, thread_id: str, limit: int = 20, order: str = 'desc', after: str = "", before: str = "") -> thread_type.RunListResponse: """ @@ -495,6 +502,7 @@ def list(self, thread_id: str, limit: int = 20, resp = thread_type.RunListResponse(**data) return resp + @assistent_tool_trace def query(self, thread_id: str, run_id: str) -> thread_type.RunResult: """ 根据thread_id和run_id,查询run的详情 diff --git a/appbuilder/core/assistant/threads/runs/steps.py b/appbuilder/core/assistant/threads/runs/steps.py index 21cf472a1..617a051ef 100644 --- a/appbuilder/core/assistant/threads/runs/steps.py +++ b/appbuilder/core/assistant/threads/runs/steps.py @@ -18,11 +18,13 @@ from appbuilder.core.assistant.type import assistant_type from appbuilder.core._client import AssistantHTTPClient from appbuilder.utils.sse_util import SSEClient +from appbuilder.utils.trace.tracer_wrapper import assistent_tool_trace class Steps(): def __init__(self) -> None: self._http_client = AssistantHTTPClient() + @assistent_tool_trace def list(self, thread_id: str, run_id: str, limit: int = 20, order: str = 'desc', after: str = "", before: str = "") -> thread_type.RunStepListResponse: """ @@ -62,6 +64,7 @@ def list(self, thread_id: str, run_id: str, limit: int = 20, resp = thread_type.RunStepListResponse(**data) return resp + @assistent_tool_trace def query(self, thread_id: str, run_id: str, step_id: str) -> thread_type.RunStepResult: """ 根据thread_id,run_id和step_id,查询对应step的信息 diff --git a/appbuilder/core/assistant/threads/threads.py b/appbuilder/core/assistant/threads/threads.py index 6a2abdc9b..f282062fb 100644 --- a/appbuilder/core/assistant/threads/threads.py +++ b/appbuilder/core/assistant/threads/threads.py @@ -18,7 +18,7 @@ from appbuilder.core.assistant.threads.messages import Messages from appbuilder.core.assistant.threads.runs import Runs from appbuilder.core._client import AssistantHTTPClient - +from appbuilder.utils.trace.tracer_wrapper import assistent_tool_trace class Threads(): def __init__(self) -> None: self._http_client = AssistantHTTPClient() @@ -31,6 +31,7 @@ def messages(self) -> Messages: def runs(self) -> Runs: return Runs() + @assistent_tool_trace def create(self, messages: Optional[list[thread_type.AssistantMessage]] = []) -> thread_type.ThreadCreateResponse: """ 创建一个新的对话线程。 @@ -69,6 +70,7 @@ def create(self, messages: Optional[list[thread_type.AssistantMessage]] = []) -> response = thread_type.ThreadCreateResponse(**data) return response + @assistent_tool_trace def query(self, thread_id:str)->thread_type.ThreadQueryResponse: """ @@ -100,6 +102,7 @@ def query(self, response = thread_type.ThreadQueryResponse(**data) return response + @assistent_tool_trace def delete(self, thread_id:str)->thread_type.ThreadDeleteResponse: """ @@ -131,6 +134,7 @@ def delete(self, response = thread_type.ThreadDeleteResponse(**data) return response + @assistent_tool_trace def update(self, thread_id:str , metadata:Optional[dict] ={} )->thread_type.ThreadUpdateResponse: diff --git a/appbuilder/core/components/animal_recognize/component.py b/appbuilder/core/components/animal_recognize/component.py index be4e1397f..974464c79 100644 --- a/appbuilder/core/components/animal_recognize/component.py +++ b/appbuilder/core/components/animal_recognize/component.py @@ -23,6 +23,7 @@ from appbuilder.core._client import HTTPClient from appbuilder.core._exception import AppBuilderServerException from typing import Generator, Union +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace TOP_NUM = 1 BAIKE_NUM = 0 @@ -81,6 +82,7 @@ class AnimalRecognition(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 动物识别 @@ -141,6 +143,7 @@ def _recognize( animalRes.request_id = request_id return animalRes + @components_run_stream_trace def tool_eval( self, name: str, diff --git a/appbuilder/core/components/asr/component.py b/appbuilder/core/components/asr/component.py index d08ce949d..0e4064837 100644 --- a/appbuilder/core/components/asr/component.py +++ b/appbuilder/core/components/asr/component.py @@ -28,6 +28,7 @@ from appbuilder.core._client import HTTPClient from appbuilder.core.components.asr.model import ShortSpeechRecognitionRequest, ShortSpeechRecognitionResponse, \ ASRInMsg, ASROutMsg +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace DEFAULT_AUDIO_MAX_DURATION = 55 * 1000 # 55s # 参考短语音极速版API(https://ai.baidu.com/ai-doc/SPEECH/Jlbxdezuf) @@ -94,6 +95,7 @@ class ASR(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, audio_format: str = "pcm", rate: int = 16000, timeout: float = None, retry: int = 0, **kwargs) -> Message: """ @@ -176,6 +178,7 @@ def _check_service_error(request_id: str, data: dict): service_err_message=data["err_msg"] ) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): """ asr for function call diff --git a/appbuilder/core/components/dish_recognize/component.py b/appbuilder/core/components/dish_recognize/component.py index fd1dece21..19ad9a2aa 100644 --- a/appbuilder/core/components/dish_recognize/component.py +++ b/appbuilder/core/components/dish_recognize/component.py @@ -25,6 +25,7 @@ from appbuilder.core._client import HTTPClient from appbuilder.core._exception import AppBuilderServerException from appbuilder.core.components.dish_recognize.model import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class DishRecognition(Component): @@ -48,6 +49,7 @@ class DishRecognition(Component): """ @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: """ 根据输入图片进行菜品识别。 diff --git a/appbuilder/core/components/doc_crop_enhance/component.py b/appbuilder/core/components/doc_crop_enhance/component.py index a2667d55a..2ad4c6f3a 100644 --- a/appbuilder/core/components/doc_crop_enhance/component.py +++ b/appbuilder/core/components/doc_crop_enhance/component.py @@ -22,6 +22,7 @@ from appbuilder.core.components.doc_crop_enhance.model import * from appbuilder.core.message import Message from appbuilder.core._exception import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace enhance_type_set = [0, 1, 2, 3] @@ -47,6 +48,7 @@ class DocCropEnhance(Component): """ @HTTPClient.check_param + @components_run_trace def run(self, message: Message, enhance_type: int = 0, timeout: float = None, retry: int = 0) -> Message: r""" 文档矫正增强 diff --git a/appbuilder/core/components/doc_format_converter/component.py b/appbuilder/core/components/doc_format_converter/component.py index a1fe3a54b..a3f5c4f2a 100644 --- a/appbuilder/core/components/doc_format_converter/component.py +++ b/appbuilder/core/components/doc_format_converter/component.py @@ -33,6 +33,7 @@ DocFormatConverterOutMessage, \ DocFormatConverterSubmitRequest, DocFormatConverterSubmitResponse, \ DocFormatConverterQueryRequest, DocFormatConverterQueryResponse +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class DocFormatConverter(Component): @@ -87,6 +88,7 @@ class DocFormatConverter(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0, request_id: str = None) -> Message: """ 将PDF、JPG、PNG、BMP等格式文件转换为Word、Excel格式,并返回转换后的文件URL。 @@ -212,6 +214,7 @@ def queryDocFormatConverterTask( response = DocFormatConverterQueryResponse.from_json(payload=json.dumps(data)) return response + @components_run_stream_trace def tool_eval(self, streaming: bool, origin_query: str, **kwargs,): """ tool eval diff --git a/appbuilder/core/components/doc_parser/doc_parser.py b/appbuilder/core/components/doc_parser/doc_parser.py index 22af93768..a18cf0102 100644 --- a/appbuilder/core/components/doc_parser/doc_parser.py +++ b/appbuilder/core/components/doc_parser/doc_parser.py @@ -26,6 +26,7 @@ from appbuilder.utils.logger_util import logger from appbuilder.core._client import HTTPClient from appbuilder.core.components.doc_parser.base import ParserConfig, ParseResult +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class DocParser(Component): @@ -100,6 +101,7 @@ def make_parse_result(self, response: Dict): return parse_result @HTTPClient.check_param + @components_run_trace def run(self, input_message: Message, return_raw=False) -> Message: """ 对传入的文件进行解析 diff --git a/appbuilder/core/components/doc_splitter/doc_splitter.py b/appbuilder/core/components/doc_splitter/doc_splitter.py index c776edbb2..47e8428d4 100644 --- a/appbuilder/core/components/doc_splitter/doc_splitter.py +++ b/appbuilder/core/components/doc_splitter/doc_splitter.py @@ -25,6 +25,7 @@ from appbuilder.core.component import Component, Message, ComponentArguments from appbuilder.utils.logger_util import logger from appbuilder.core.components.doc_parser.base import DocSegment +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class DocSplitter(Component): @@ -58,6 +59,7 @@ def __init__(self, splitter_type, max_segment_length=800, overlap=200, super(DocSplitter, self). __init__(meta=self.meta, **kwargs) + @components_run_trace def run(self, message: Message): """ 对输入的解析文档结果,处理为多个段落结果 @@ -144,6 +146,7 @@ def __init__(self, max_segment_length=800, overlap=200, super(ChunkSplitter, self). __init__(meta=self.meta, **kwargs) + @components_run_trace def run(self, message: Message): """ 对输入的解析文档结果,按照最大段落块大小、结尾分隔符等,处理为多个段落结果 @@ -254,6 +257,7 @@ def inner_get_titles(nodes, parent_id, titles): return titles[::-1] # 按照标题层级进行切分 + @components_run_trace def run(self, input_message: Message) -> Message: """ 对输入的解析文档结果,按照各标题层级,处理为多个段落结果 diff --git a/appbuilder/core/components/embeddings/component.py b/appbuilder/core/components/embeddings/component.py index 1d468313a..4add3872c 100644 --- a/appbuilder/core/components/embeddings/component.py +++ b/appbuilder/core/components/embeddings/component.py @@ -22,6 +22,7 @@ from appbuilder.core.components.embeddings.base import EmbeddingBaseComponent from appbuilder.core.component import ComponentArguments from appbuilder.core._exception import AppBuilderServerException, ModelNotSupportedException +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class EmbeddingArgs(ComponentArguments): @@ -129,6 +130,7 @@ def _batch(self, texts: List[str]) -> Message[List[List[float]]]: return results + @components_run_trace def run(self, text: Union[Message[str], str]) -> Message[List[float]]: """ run diff --git a/appbuilder/core/components/excel2figure/component.py b/appbuilder/core/components/excel2figure/component.py index 0e8281d1c..d0e74380a 100644 --- a/appbuilder/core/components/excel2figure/component.py +++ b/appbuilder/core/components/excel2figure/component.py @@ -26,6 +26,7 @@ from appbuilder.core.component import Component, ComponentArguments from appbuilder.core.message import Message from appbuilder.core.utils import ModelInfo, ttl_lru_cache +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class Excel2FigureArgs(ComponentArguments): @@ -96,6 +97,7 @@ def _check_model_and_get_model_url(self, model, model_type): model_url = self.model_info.get_model_url(model) return model_url + @components_run_trace def run(self, message: Message) -> Message: """ 执行 excel2figure @@ -191,6 +193,7 @@ def _run_excel2figure(self, query: str, excel_file_url: str, model: str, excel_f f"failed to generate figure for query={query}, excel_file_url={excel_file_url}") return Message(figure_url) + @components_run_stream_trace def tool_eval( self, streaming: bool, diff --git a/appbuilder/core/components/extract_table/component.py b/appbuilder/core/components/extract_table/component.py index 469bb25ab..30dad487e 100644 --- a/appbuilder/core/components/extract_table/component.py +++ b/appbuilder/core/components/extract_table/component.py @@ -22,6 +22,7 @@ from appbuilder.core.component import Component, Message, ComponentArguments from appbuilder.utils.logger_util import logger +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class ExtractTableFromDoc(Component): @@ -96,6 +97,7 @@ def _post_process(self, resp): data.append(tmp) return data + @components_run_trace def run(self, message: Message, table_max_size: int = 800, doc_node_num_before_table: int = 1): """ 将文档原始解析结果,请求云端进行表格抽取,返回表格列表。 diff --git a/appbuilder/core/components/gbi/nl2sql/component.py b/appbuilder/core/components/gbi/nl2sql/component.py index 6641fe464..404f8463d 100644 --- a/appbuilder/core/components/gbi/nl2sql/component.py +++ b/appbuilder/core/components/gbi/nl2sql/component.py @@ -23,6 +23,7 @@ from appbuilder.core.components.gbi.basic import ColumnItem from appbuilder.core.components.gbi.basic import NL2SqlResult from appbuilder.core.components.gbi.basic import SUPPORTED_MODEL_NAME +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class NL2SqlArgs(ComponentArguments): @@ -79,6 +80,7 @@ def __init__(self, model_name: str, table_schemas: List[str], knowledge: Dict = self.knowledge = knowledge or dict() self.prompt_template = prompt_template + @components_run_trace def run(self, message: Message, timeout: float = 60, retry: int = 0) -> Message[NL2SqlResult]: """ diff --git a/appbuilder/core/components/gbi/select_table/component.py b/appbuilder/core/components/gbi/select_table/component.py index 14a4b8c57..c9eca8bdd 100644 --- a/appbuilder/core/components/gbi/select_table/component.py +++ b/appbuilder/core/components/gbi/select_table/component.py @@ -21,7 +21,7 @@ from appbuilder.core.message import Message from appbuilder.core.components.gbi.basic import SessionRecord from appbuilder.core.components.gbi.basic import SUPPORTED_MODEL_NAME - +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class SelectTableArgs(ComponentArguments): """ @@ -76,6 +76,7 @@ def __init__(self, model_name: str, table_descriptions: Dict[str, str], self.table_descriptions = table_descriptions self.prompt_template = prompt_template + @components_run_trace def run(self, message: Message, timeout: int = 60, retry: int = 0) -> Message[List[str]]: """ diff --git a/appbuilder/core/components/general_ocr/component.py b/appbuilder/core/components/general_ocr/component.py index ce784acff..54e790a57 100644 --- a/appbuilder/core/components/general_ocr/component.py +++ b/appbuilder/core/components/general_ocr/component.py @@ -23,6 +23,7 @@ from appbuilder.core.component import Component from appbuilder.core.components.general_ocr.model import * from appbuilder.core.message import Message +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class GeneralOCR(Component): @@ -82,6 +83,7 @@ class GeneralOCR(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 输入图片并识别其中的文字 @@ -156,6 +158,7 @@ def _check_service_error(request_id: str, data: dict): service_err_message=data.get("error_msg") ) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): """ general_ocr for function call diff --git a/appbuilder/core/components/handwrite_ocr/component.py b/appbuilder/core/components/handwrite_ocr/component.py index 4101e8de9..88e8790b2 100644 --- a/appbuilder/core/components/handwrite_ocr/component.py +++ b/appbuilder/core/components/handwrite_ocr/component.py @@ -19,6 +19,7 @@ from appbuilder.core.message import Message from appbuilder.core._client import HTTPClient from appbuilder.core import utils +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class HandwriteOCR(Component): r""" 手写文字识别组件 @@ -63,6 +64,7 @@ class HandwriteOCR(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 输入图片并识别其中的文字 @@ -99,6 +101,7 @@ def run(self, message: Message, timeout: float = None, retry: int = 0) -> Messag for w in response.words_result] return Message(content=out.model_dump()) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): traceid = kwargs.get("traceid") diff --git a/appbuilder/core/components/image_understand/component.py b/appbuilder/core/components/image_understand/component.py index 53434ab5d..addac5c67 100644 --- a/appbuilder/core/components/image_understand/component.py +++ b/appbuilder/core/components/image_understand/component.py @@ -22,6 +22,7 @@ from appbuilder.core._exception import AppBuilderServerException from appbuilder.core.components.image_understand.model import * from typing import Generator, Union +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class ImageUnderstand(Component): @@ -80,6 +81,7 @@ class ImageUnderstand(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 执行图像内容理解 @@ -161,6 +163,7 @@ def __recognize( # 避免触发限流(>1QPS),等待1.1秒 time.sleep(1.1) + @components_run_stream_trace def tool_eval( self, name: str, diff --git a/appbuilder/core/components/landmark_recognize/component.py b/appbuilder/core/components/landmark_recognize/component.py index f11322aad..7088ee5d8 100644 --- a/appbuilder/core/components/landmark_recognize/component.py +++ b/appbuilder/core/components/landmark_recognize/component.py @@ -20,6 +20,7 @@ from appbuilder.core._client import HTTPClient from appbuilder.core._exception import AppBuilderServerException from appbuilder.core.components.landmark_recognize.model import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class LandmarkRecognition(Component): @@ -41,6 +42,7 @@ class LandmarkRecognition(Component): """ @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 输入图片并识别其中的地标 diff --git a/appbuilder/core/components/llms/dialog_summary/component.py b/appbuilder/core/components/llms/dialog_summary/component.py index 08d542ddf..082bf3c59 100644 --- a/appbuilder/core/components/llms/dialog_summary/component.py +++ b/appbuilder/core/components/llms/dialog_summary/component.py @@ -20,6 +20,7 @@ from appbuilder.core.components.llms.base import CompletionBaseComponent from appbuilder.core.message import Message from appbuilder.core.component import ComponentArguments +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class DialogSummaryArgs(ComponentArguments): @@ -75,6 +76,7 @@ def __init__( super().__init__( DialogSummaryArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0): """ 使用给定的输入运行模型并返回结果。 diff --git a/appbuilder/core/components/llms/hallucination_detection/component.py b/appbuilder/core/components/llms/hallucination_detection/component.py index a05c8dd83..08aec76ae 100644 --- a/appbuilder/core/components/llms/hallucination_detection/component.py +++ b/appbuilder/core/components/llms/hallucination_detection/component.py @@ -23,6 +23,7 @@ from appbuilder.core.component import ComponentArguments from appbuilder.core._exception import AppBuilderServerException from appbuilder.utils.logger_util import logger +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class HallucinationDetectionArgs(ComponentArguments): """幻觉检测配置 @@ -158,6 +159,7 @@ def completion(self, version, base_url, request, timeout: float = None, response)) return self.gene_response(response, stream) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0.0): """ 使用给定的输入运行模型并返回结果。 @@ -191,6 +193,7 @@ def run(self, message, stream=False, temperature=1e-10, top_p=0.0): return result + @components_run_stream_trace def tool_eval(self, name: str, stream: bool = False, **kwargs): """ tool_eval for function call diff --git a/appbuilder/core/components/llms/is_complex_query/component.py b/appbuilder/core/components/llms/is_complex_query/component.py index f268d814d..983e57643 100644 --- a/appbuilder/core/components/llms/is_complex_query/component.py +++ b/appbuilder/core/components/llms/is_complex_query/component.py @@ -20,6 +20,7 @@ from appbuilder.core.message import Message from appbuilder.core.component import ComponentArguments from appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class IsComplexQueryMeta(ComponentArguments): @@ -76,6 +77,7 @@ def __init__( super().__init__( IsComplexQueryMeta, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0): """ 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 diff --git a/appbuilder/core/components/llms/mrc/component.py b/appbuilder/core/components/llms/mrc/component.py index 5eaa112b4..837bc7943 100644 --- a/appbuilder/core/components/llms/mrc/component.py +++ b/appbuilder/core/components/llms/mrc/component.py @@ -18,6 +18,7 @@ from appbuilder.core.component import ComponentArguments from pydantic import Field from typing import Optional +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class MrcArgs(ComponentArguments): @@ -122,6 +123,7 @@ def __get_instruction_set(self): "cite": "使用引用标记来标注回答内容参考的搜索结果序号,例如^[1]^ (引用单个搜索结果),^[1][2]^(引用多个搜索结果)," "其中方括号中的数字是搜索结果序号。引用标记只能出现在句尾标点符号前。"} + @components_run_trace def run(self, message, context_list, reject=False, clarify=False, highlight=False, friendly=False, cite=False, stream=False, temperature=1e-10, top_p=0): diff --git a/appbuilder/core/components/llms/nl2pandas/component.py b/appbuilder/core/components/llms/nl2pandas/component.py index ca4569ba7..65d647cfc 100644 --- a/appbuilder/core/components/llms/nl2pandas/component.py +++ b/appbuilder/core/components/llms/nl2pandas/component.py @@ -22,6 +22,7 @@ from pydantic import BaseModel, Field from typing import Optional from appbuilder.core.component import ComponentArguments +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class Nl2pandasArgs(ComponentArguments): @@ -84,6 +85,7 @@ def __init__( super().__init__( Nl2pandasArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, table_info=None, stream=False, temperature=1e-10, top_p=0): """ 使用给定的输入运行模型并返回结果。 diff --git a/appbuilder/core/components/llms/oral_query_generation/component.py b/appbuilder/core/components/llms/oral_query_generation/component.py index 6dc94a1d1..1c91917e1 100644 --- a/appbuilder/core/components/llms/oral_query_generation/component.py +++ b/appbuilder/core/components/llms/oral_query_generation/component.py @@ -27,6 +27,7 @@ from appbuilder.core.component import ComponentArguments from appbuilder.core._exception import AppBuilderServerException from appbuilder.utils.logger_util import logger +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class QueryTypeChoices(Enum): @@ -220,6 +221,7 @@ def completion(self, version, base_url, request, timeout: float = None, response)) return self.gene_response(response, stream) + @components_run_trace def run(self, message, query_type='全部', output_format='str', stream=False, temperature=1e-10, top_p=0.0): """ 使用给定的输入运行模型并返回结果。 @@ -257,6 +259,7 @@ def run(self, message, query_type='全部', output_format='str', stream=False, t return result + @components_run_stream_trace def tool_eval(self, name: str, stream: bool = False, **kwargs): """ tool_eval for function call diff --git a/appbuilder/core/components/llms/playground/component.py b/appbuilder/core/components/llms/playground/component.py index c9a652d68..6f2a31612 100644 --- a/appbuilder/core/components/llms/playground/component.py +++ b/appbuilder/core/components/llms/playground/component.py @@ -18,6 +18,7 @@ from appbuilder.core.component import ComponentArguments from appbuilder.core.components.llms.base import CompletionBaseComponent from appbuilder.core.message import Message +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class PlaygroundArgs(ComponentArguments): @@ -82,6 +83,7 @@ def __init__( self.variable_names = self.__parse__(prompt_template) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0.0): """ 使用给定的输入运行模型并返回结果。 diff --git a/appbuilder/core/components/llms/qa_pair_mining/component.py b/appbuilder/core/components/llms/qa_pair_mining/component.py index d68fde065..f6c748d88 100644 --- a/appbuilder/core/components/llms/qa_pair_mining/component.py +++ b/appbuilder/core/components/llms/qa_pair_mining/component.py @@ -20,6 +20,7 @@ from appbuilder.core.message import Message from appbuilder.core.component import ComponentArguments from appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class QAPairMiningMeta(ComponentArguments): @@ -78,6 +79,7 @@ def __init__( super().__init__( QAPairMiningMeta, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0.0): """ 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 diff --git a/appbuilder/core/components/llms/query_decomposition/component.py b/appbuilder/core/components/llms/query_decomposition/component.py index 9bb54eb7b..0ea88f17f 100644 --- a/appbuilder/core/components/llms/query_decomposition/component.py +++ b/appbuilder/core/components/llms/query_decomposition/component.py @@ -20,6 +20,7 @@ from appbuilder.core.message import Message from appbuilder.core.component import ComponentArguments from appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class QueryDecompositionMeta(ComponentArguments): @@ -76,6 +77,7 @@ def __init__( super().__init__( QueryDecompositionMeta, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0.0): """ 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 diff --git a/appbuilder/core/components/llms/query_rewrite/component.py b/appbuilder/core/components/llms/query_rewrite/component.py index c48430c55..05337c6ae 100644 --- a/appbuilder/core/components/llms/query_rewrite/component.py +++ b/appbuilder/core/components/llms/query_rewrite/component.py @@ -24,6 +24,7 @@ from typing import Optional from enum import Enum import appbuilder +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class RewriteTypeChoices(Enum): """""" @@ -107,6 +108,7 @@ def __init__( super().__init__( QueryRewriteArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, rewrite_type="带机器人回复", stream=False, temperature=1e-10, top_p=0): """ 使用给定的输入运行模型并返回结果。输入列表长度不超过10,字符总长度不超过5000. diff --git a/appbuilder/core/components/llms/similar_question/component.py b/appbuilder/core/components/llms/similar_question/component.py index c181c3d10..6efe46f17 100644 --- a/appbuilder/core/components/llms/similar_question/component.py +++ b/appbuilder/core/components/llms/similar_question/component.py @@ -22,6 +22,7 @@ from appbuilder.core.message import Message from appbuilder.core.component import ComponentArguments from appbuilder.core.components.llms.base import CompletionBaseComponent +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class SimilarQuestionMeta(ComponentArguments): @@ -95,6 +96,7 @@ def __init__( super().__init__( SimilarQuestionMeta, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0.0, request_id=None): """ 给定输入(message)到模型运行,同时指定运行参数,并返回结果。 @@ -110,6 +112,7 @@ def run(self, message, stream=False, temperature=1e-10, top_p=0.0, request_id=No """ return super().run(message=message, stream=stream, temperature=temperature, top_p=top_p, request_id=request_id) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool = False, **kwargs): """ tool_eval for function call diff --git a/appbuilder/core/components/llms/style_rewrite/component.py b/appbuilder/core/components/llms/style_rewrite/component.py index 38b84a2b3..036f094c8 100644 --- a/appbuilder/core/components/llms/style_rewrite/component.py +++ b/appbuilder/core/components/llms/style_rewrite/component.py @@ -23,6 +23,7 @@ from pydantic import BaseModel, Field from enum import Enum from typing import Optional +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class StyleChoices(Enum): @@ -132,6 +133,7 @@ def __init__( StyleRewriteArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, style="营销话术", stream=False, temperature=1e-10, top_p=0.0, request_id=None): """ 使用给定的输入运行模型并返回结果。 @@ -149,6 +151,7 @@ def run(self, message, style="营销话术", stream=False, temperature=1e-10, to """ return super().run(message=message, style=style, stream=stream, temperature=temperature, top_p=top_p, request_id=request_id) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool = False, **kwargs): """ tool_eval for function call diff --git a/appbuilder/core/components/llms/style_writing/component.py b/appbuilder/core/components/llms/style_writing/component.py index 97236225d..c05a0743a 100644 --- a/appbuilder/core/components/llms/style_writing/component.py +++ b/appbuilder/core/components/llms/style_writing/component.py @@ -19,6 +19,7 @@ from appbuilder.core.message import Message from appbuilder.core.component import ComponentArguments +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace from pydantic import BaseModel, Field from typing import Optional @@ -160,6 +161,7 @@ def __init__( StyleWritingArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, style_query="通用", length=100, stream=False, temperature=1e-10, top_p=0, request_id=None): """ 使用给定的输入运行模型并返回结果。 @@ -178,6 +180,7 @@ def run(self, message, style_query="通用", length=100, stream=False, temperatu return super().run(message=message, style_query=style_query, length=length, stream=stream, temperature=temperature, top_p=top_p, request_id=request_id) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool = False, **kwargs): """ tool_eval for function call diff --git a/appbuilder/core/components/llms/tag_extraction/component.py b/appbuilder/core/components/llms/tag_extraction/component.py index a69c4be2a..2aac61559 100644 --- a/appbuilder/core/components/llms/tag_extraction/component.py +++ b/appbuilder/core/components/llms/tag_extraction/component.py @@ -18,6 +18,7 @@ from appbuilder.core.component import ComponentArguments from pydantic import BaseModel, Field from typing import Optional +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class TagExtractionArgs(ComponentArguments): @@ -72,6 +73,7 @@ def __init__( super().__init__( TagExtractionArgs, model=model, secret_key=secret_key, gateway=gateway, lazy_certification=lazy_certification) + @components_run_trace def run(self, message, stream=False, temperature=1e-10, top_p=0.0): """ 使用给定的输入运行模型并返回结果。 diff --git a/appbuilder/core/components/matching/component.py b/appbuilder/core/components/matching/component.py index 170101578..4fefccf30 100644 --- a/appbuilder/core/components/matching/component.py +++ b/appbuilder/core/components/matching/component.py @@ -23,6 +23,7 @@ from appbuilder.core.message import Message from appbuilder.core.components.embeddings import EmbeddingBaseComponent +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace from .base import MatchingBaseComponent, MatchingArgs @@ -69,6 +70,7 @@ def __init__( self.embedding_component = embedding_component super().__init__(self.meta) + @components_run_trace def run( self, query: Union[Message[str], str], diff --git a/appbuilder/core/components/mix_card_ocr/component.py b/appbuilder/core/components/mix_card_ocr/component.py index f6b8783d0..ef14140a3 100644 --- a/appbuilder/core/components/mix_card_ocr/component.py +++ b/appbuilder/core/components/mix_card_ocr/component.py @@ -20,6 +20,7 @@ from appbuilder.core.component import Component from appbuilder.core.components.mix_card_ocr.model import * from appbuilder.core.message import Message +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class MixCardOCR(Component): @@ -69,6 +70,7 @@ class MixCardOCR(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 输入图片并识别身份证信息 @@ -149,6 +151,7 @@ def _check_service_error(request_id: str, data: dict): service_err_message=data.get("error_msg") ) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): result = {} traceid = kwargs.get("traceid") diff --git a/appbuilder/core/components/object_recognize/component.py b/appbuilder/core/components/object_recognize/component.py index 9bd0584bc..efa7187bb 100644 --- a/appbuilder/core/components/object_recognize/component.py +++ b/appbuilder/core/components/object_recognize/component.py @@ -22,7 +22,7 @@ from appbuilder.core.message import Message from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError from appbuilder.core.components.object_recognize.model import * - +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class ObjectRecognition(Component): r""" @@ -79,6 +79,7 @@ class ObjectRecognition(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 通用物体识别 @@ -145,6 +146,7 @@ def _check_service_error(request_id: str, data: dict): service_err_message=data.get("error_msg") ) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): """ object_recognize for function call diff --git a/appbuilder/core/components/plant_recognize/component.py b/appbuilder/core/components/plant_recognize/component.py index 15fee0f1b..4ad0f5f3c 100644 --- a/appbuilder/core/components/plant_recognize/component.py +++ b/appbuilder/core/components/plant_recognize/component.py @@ -21,6 +21,7 @@ from appbuilder.core._exception import AppBuilderServerException from appbuilder.core.components.plant_recognize.model import * from typing import Generator, Union +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace TOP_NUM = 1 BAIKE_NUM = 0 @@ -88,6 +89,7 @@ class PlantRecognition(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 输入图片并识别其中的植物 @@ -145,6 +147,7 @@ def __recognize( self.__class__.__check_service_error(request_id, data) return PlantRecognitionResponse(data, request_id=request_id) + @components_run_stream_trace def tool_eval( self, name: str, diff --git a/appbuilder/core/components/qrcode_ocr/component.py b/appbuilder/core/components/qrcode_ocr/component.py index 735c76fc6..0219e0680 100644 --- a/appbuilder/core/components/qrcode_ocr/component.py +++ b/appbuilder/core/components/qrcode_ocr/component.py @@ -23,6 +23,7 @@ from appbuilder.core.message import Message from appbuilder.core._client import HTTPClient from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class QRcodeOCR(Component): @@ -72,6 +73,7 @@ class QRcodeOCR(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, location: str = "true", timeout: float = None, retry: int = 0) -> Message: r""" 二维码识别 @@ -149,6 +151,7 @@ def _check_service_error(request_id: str, data: dict): service_err_message=data.get("error_msg") ) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): result = {} traceid = kwargs.get("traceid") diff --git a/appbuilder/core/components/rag_with_baidu_search/component.py b/appbuilder/core/components/rag_with_baidu_search/component.py index af6b6b475..b20a63f4e 100644 --- a/appbuilder/core/components/rag_with_baidu_search/component.py +++ b/appbuilder/core/components/rag_with_baidu_search/component.py @@ -20,6 +20,7 @@ from appbuilder.core.message import Message from pydantic import Field from typing import Optional +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class RAGWithBaiduSearchArgs(ComponentArguments): @@ -131,6 +132,7 @@ def _get_search_input(self, text): max_bytes -= 1 return "" + @components_run_trace def run( self, message, diff --git a/appbuilder/core/components/rag_with_baidu_search_pro/component.py b/appbuilder/core/components/rag_with_baidu_search_pro/component.py index de8fe5e71..053faddf8 100644 --- a/appbuilder/core/components/rag_with_baidu_search_pro/component.py +++ b/appbuilder/core/components/rag_with_baidu_search_pro/component.py @@ -19,6 +19,7 @@ from appbuilder.core.utils import ModelInfo, ttl_lru_cache from appbuilder.core._exception import AppBuilderServerException from appbuilder.core.components.rag_with_baidu_search_pro.parse_rag_pro_response import ParseRagProResponse +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace from pydantic import BaseModel, Field, conint, confloat from typing import Optional @@ -67,6 +68,7 @@ def set_secret_key_and_gateway(self, secret_key: Optional[str] = None, gateway: secret_key=secret_key, gateway=gateway) self.__class__.model_info = ModelInfo(client=self.http_client) + @components_run_trace def run( self, message, diff --git a/appbuilder/core/components/retriever/baidu_vdb/baiduvdb_retriever.py b/appbuilder/core/components/retriever/baidu_vdb/baiduvdb_retriever.py index 0f6d1ec29..3ffe5f2b9 100644 --- a/appbuilder/core/components/retriever/baidu_vdb/baiduvdb_retriever.py +++ b/appbuilder/core/components/retriever/baidu_vdb/baiduvdb_retriever.py @@ -27,6 +27,7 @@ from appbuilder.core.components.embeddings.component import Embedding from appbuilder.core.constants import GATEWAY_URL from appbuilder.utils.logger_util import logger +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace DEFAULT_ACCOUNT = "root" DEFAULT_DATABASE_NAME = "AppBuilderDatabase" @@ -445,6 +446,7 @@ def __init__(self, embedding, table): self.embedding = embedding self.table = table + @components_run_trace def run(self, query: Message, top_k: int = 1): """ 根据query进行查询 diff --git a/appbuilder/core/components/retriever/bes/bes_retriever.py b/appbuilder/core/components/retriever/bes/bes_retriever.py index b97feec33..f98e4abf1 100644 --- a/appbuilder/core/components/retriever/bes/bes_retriever.py +++ b/appbuilder/core/components/retriever/bes/bes_retriever.py @@ -28,6 +28,7 @@ from appbuilder.core.constants import GATEWAY_URL from appbuilder.utils.logger_util import logger from appbuilder import get_default_header +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class BESVectorStoreIndex: @@ -236,6 +237,7 @@ def __init__(self, embedding, index_name, bes_client, index_type="hnsw"): self.bes_client = bes_client self.index_type = index_type + @components_run_trace def run(self, query: Message, top_k: int = 1): """ 根据query进行查询 diff --git a/appbuilder/core/components/table_ocr/component.py b/appbuilder/core/components/table_ocr/component.py index 2c2b3f716..2255f7b43 100644 --- a/appbuilder/core/components/table_ocr/component.py +++ b/appbuilder/core/components/table_ocr/component.py @@ -23,6 +23,7 @@ from appbuilder.core.message import Message from appbuilder.core._client import HTTPClient from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class TableOCR(Component): @@ -68,6 +69,7 @@ class TableOCR(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, timeout: float = None, retry: int = 0) -> Message: r""" 表格文字识别 @@ -176,6 +178,7 @@ def get_table_markdown(self, tables_result): markdowns.append(markdown_table) return markdowns + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): result = {} traceid = kwargs.get("traceid") diff --git a/appbuilder/core/components/text_to_image/component.py b/appbuilder/core/components/text_to_image/component.py index aae16b95f..56cd34aba 100644 --- a/appbuilder/core/components/text_to_image/component.py +++ b/appbuilder/core/components/text_to_image/component.py @@ -23,6 +23,7 @@ from appbuilder.core._exception import AppBuilderServerException, RiskInputException from appbuilder.core.components.text_to_image.model import Text2ImageSubmitRequest, Text2ImageQueryRequest, \ Text2ImageQueryResponse, Text2ImageSubmitResponse, Text2ImageOutMessage, Text2ImageInMessage +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class Text2Image(Component): @@ -63,6 +64,7 @@ class Text2Image(Component): ] @HTTPClient.check_param + @components_run_trace def run( self, message: Message, diff --git a/appbuilder/core/components/translate/component.py b/appbuilder/core/components/translate/component.py index 2ed9a6ce8..1d6b171e4 100644 --- a/appbuilder/core/components/translate/component.py +++ b/appbuilder/core/components/translate/component.py @@ -24,6 +24,7 @@ from appbuilder.core._client import HTTPClient from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError from appbuilder.core.components.translate.model import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class Translation(Component): @@ -82,6 +83,7 @@ class Translation(Component): ] @HTTPClient.check_param + @components_run_trace def run(self, message: Message, from_lang: str = "auto", to_lang: str = "en", timeout: float = None, retry: int = 0) -> Message: """ @@ -146,6 +148,7 @@ def _translate(self, request: TranslateRequest, timeout: float = None, json_str = json.dumps(data) return TranslateResponse(TranslateResponse.from_json(json_str)) + @components_run_stream_trace def tool_eval(self, name: str, streaming: bool, **kwargs): """ translate for function call diff --git a/appbuilder/core/components/tts/component.py b/appbuilder/core/components/tts/component.py index fafd514f2..b144cd40d 100644 --- a/appbuilder/core/components/tts/component.py +++ b/appbuilder/core/components/tts/component.py @@ -23,6 +23,7 @@ from appbuilder.core.message import Message from appbuilder.core._exception import AppBuilderServerException, InvalidRequestArgumentError from appbuilder.core.components.tts.model import * +from appbuilder.utils.trace.tracer_wrapper import components_run_trace, components_run_stream_trace class TTS(Component): @@ -66,6 +67,7 @@ def __init__(self, *args, **kwargs): self.model = "" @HTTPClient.check_param + @components_run_trace def run(self, message: Message, model: Literal["baidu-tts", "paddlespeech-tts"] = "baidu-tts", diff --git a/appbuilder/tests/test_appbuilder_assistant_trace.py b/appbuilder/tests/test_appbuilder_assistant_trace.py new file mode 100644 index 000000000..3fc8ad8ab --- /dev/null +++ b/appbuilder/tests/test_appbuilder_assistant_trace.py @@ -0,0 +1,82 @@ +# Copyright (c) 2024 Baidu, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import os +import unittest +import appbuilder +from appbuilder import AppBuilderTracer + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderTrace(unittest.TestCase): + def setUp(self): + """ + 设置测试环境所需的变量。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ + os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] + + def test_appbuilder_assistant_trace(self): + """ + 测试AppBuilder Assistant的追踪功能。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ + tracer=AppBuilderTracer( + enable_phoenix = True, + enable_console = True, + ) + + tracer.start_trace() + + assistant = appbuilder.assistant.assistants.create( + name="test_assistant", + description="test assistant", + instructions="每句话回复前都加上我是秦始皇" + ) + + file = appbuilder.assistant.assistants.files.create( + "./data/qa_doc_parser_extract_table_from_doc.png" + ) + + thread = appbuilder.assistant.threads.create() + appbuilder.assistant.threads.messages.create( + thread_id=thread.id, + content="hello world", + file_ids=[file.id] + ) + + run_result = appbuilder.assistant.threads.runs.run( + thread_id=thread.id, + assistant_id=assistant.id + ) + + generator = appbuilder.assistant.threads.runs.stream_run( + thread_id=thread.id, + assistant_id=assistant.id + ) + + tracer.end_trace() + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/appbuilder/tests/test_appbuilder_trace.py b/appbuilder/tests/test_appbuilder_client_trace.py similarity index 64% rename from appbuilder/tests/test_appbuilder_trace.py rename to appbuilder/tests/test_appbuilder_client_trace.py index 692d660f6..ff613e266 100644 --- a/appbuilder/tests/test_appbuilder_trace.py +++ b/appbuilder/tests/test_appbuilder_client_trace.py @@ -13,22 +13,39 @@ # limitations under the License. import unittest -import requests import appbuilder import os -from unittest.mock import patch,MagicMock - from appbuilder.utils.trace.tracer import AppBuilderTracer, AppbuilderInstrumentor -from appbuilder.utils.trace.phoenix_wrapper import runtime_main,stop_phoenix +from appbuilder.utils.trace.phoenix_wrapper import runtime_main,stop_phoenix,launch_phoenix from appbuilder.core.console.appbuilder_client import get_app_list @unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") class TestAppBuilderTrace(unittest.TestCase): def setUp(self): + """ + 初始化方法,用于设置测试前的环境。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ self.app_id = "2a19f6dd-de02-46d9-841d-ef5c52b00466" def test_appbuilder_client_trace(self): + """ + 测试AppBuilderClient的跟踪功能 + + Args: + 无 + + Returns: + 无返回值,该函数主要用于测试跟踪功能 + + """ tracer=AppBuilderTracer( enable_phoenix = True, @@ -54,25 +71,43 @@ def test_appbuilder_client_trace(self): tracer.end_trace() - - def test_client_trace_function(self): - from appbuilder.utils.trace._function import _client_tool_trace_output,_client_tool_trace_output_deep_iterate - class Test: - test = 'test' - _client_tool_trace_output(Test,None) - - _client_tool_trace_output_deep_iterate({},None) - def test_trace_tracer(self): + """ + 测试AppbuilderInstrumentor类的trace_tracer方法。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ tracer=AppbuilderInstrumentor() tracer.instrumentation_dependencies() tracer._instrument() def test_appbuilder_phoenix_run(self): - - runtime_main() - with self.assertRaises(TypeError): + """ + 测试appbuilder_phoenix_run方法 + + Args: + 无参数。 + + Returns: + 无返回值。 + + Raises: + TypeError: 当调用runtime_main()或stop_phoenix()函数时,预期会抛出TypeError异常。 + + """ + with self.assertRaises(ImportError): + runtime_main() + + with self.assertRaises(ImportError): + launch_phoenix() + + with self.assertRaises(ImportError): stop_phoenix() diff --git a/appbuilder/tests/test_appbuilder_components_trace.py b/appbuilder/tests/test_appbuilder_components_trace.py new file mode 100644 index 000000000..73f7e6dcd --- /dev/null +++ b/appbuilder/tests/test_appbuilder_components_trace.py @@ -0,0 +1,111 @@ +# Copyright (c) 2024 Baidu, Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import unittest +import requests +import appbuilder +import os + +from appbuilder import AppBuilderTracer + +TEST_QUERY = '澳门新麻蒲烤肉店每天开门吗?' +TEST_CONTEXT = \ +('澳门美食: 澳门新麻蒲韩国烤肉店\n' +'在澳门一年四季之中除了火锅,烤肉也相当受欢迎。提到韩烧,有一间令我印象最深刻,就是号称韩国第一的烤肉店-新麻蒲韩国烤肉店,光是韩国的分店便多' +'达四百多间,海外分店更是遍布世界各地,2016年便落户澳门筷子基区,在原本已经食肆林立的地方一起百花齐放!店内的装修跟韩国分店还完度几乎没差,让' +'食客彷如置身于韩国的感觉,还要大赞其抽风系统不俗,离开时身上都不会沾上烤肉味耶!\n' +'时间:周一至周日 下午5:00 - 上午3:00\n' +'电话:+853 2823 4012\n' +'地址:澳门筷子基船澳街海擎天第三座地下O号铺96号\n' +'必食推介:\n' +'护心肉二人套餐\n' +'来新麻蒲必试的有两样东西,现在差不多每间烤肉店都有炉边烤蛋,但大家知道吗?原来新麻蒲就是炉边烤蛋的开创者,既然是始祖,这已经是个非吃不可的理' +'由!还有一款必试的就是护心肉,即是猪的横隔膜与肝中间的部分,每头猪也只有200克这种肉,非常珍贵,其味道吃起来有种独特的肉香味,跟牛护心肉一样' +'精彩!\n' +'秘制猪皮\n' +'很多怕胖的女生看到猪皮就怕怕,但其实猪皮含有大量胶原蛋白,营养价值很高呢!这里红通通的猪皮还经过韩国秘制酱汁处理过,会有一点点辣味。烤猪皮的' +'时候也需特别注意火侯,这样吃起来才会有外脆内Q的口感!') +TEST_ANSWER = '澳门新麻蒲烤肉店并不是每天开门,周日休息。' + +@unittest.skipUnless(os.getenv("TEST_CASE", "UNKNOWN") == "CPU_SERIAL", "") +class TestAppBuilderComponentsTrace(unittest.TestCase): + def setUp(self): + """ + 初始化函数,用于设置测试所需的变量和对象。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ + self.audio_file_url = "https://bj.bcebos.com/v1/appbuilder/asr_test.pcm?authorization=bce-auth-v1" \ + "%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-01-11T10%3A56%3A41Z%2F-1%2Fhost" \ + "%2Fa6c4d2ca8a3f0259f4cae8ae3fa98a9f75afde1a063eaec04847c99ab7d1e411" + self.asr = appbuilder.ASR() + self.play = appbuilder.Playground(prompt_template="你好,{name},我是{bot_name},{bot_name}是一个{bot_type},我可以{bot_function},你可以问我{bot_question}。", model='eb-4') + model_name = 'ERNIE Speed-AppBuilder' + secret_key = os.getenv('SECRET_KEY', None) + self.hallucination_detection = appbuilder.HallucinationDetection(model=model_name, secret_key=secret_key) + + def test_trace(self): + """ + 测试追踪功能,包括ASR运行、工具评估、playground运行和幻觉检测工具评估。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + """ + tracer=AppBuilderTracer( + enable_phoenix = True, + enable_console = True, + ) + + tracer.start_trace() + + # test asr run and tool_eval + raw_audio = requests.get(self.audio_file_url).content + inp = appbuilder.Message(content={"raw_audio": raw_audio}) + out = self.asr.run(inp) + result = self.asr.tool_eval(name="asr", streaming=True, file_url=self.audio_file_url) + + # test playground run + msg = appbuilder.Message({ + "name": "小明", + "bot_name": "机器人", + "bot_type": "聊天机器人", + "bot_function": "聊天", + "bot_question": "你好吗?" + }) + + answer = self.play.run(message=msg, stream=False, temperature=1) + + + # test hallucination_detection tool_eval + query = TEST_QUERY + context = TEST_CONTEXT + answer = TEST_ANSWER + model_configs = {'temperature': 0.5, 'top_p': 0.5} + answer = self.hallucination_detection.tool_eval(name='', + stream=True, + query=query, + context=context, + answer=answer, + model_configs=model_configs) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file diff --git a/appbuilder/tests/test_assistant_e2e_stream_event_handler.py b/appbuilder/tests/test_assistant_e2e_stream_event_handler.py index 5468fb0a8..28d383871 100644 --- a/appbuilder/tests/test_assistant_e2e_stream_event_handler.py +++ b/appbuilder/tests/test_assistant_e2e_stream_event_handler.py @@ -61,7 +61,14 @@ class TestFunctionCall(unittest.TestCase): def setUp(self): os.environ["APPBUILDER_TOKEN"] = os.environ["APPBUILDER_TOKEN_V2"] - def test_end_to_end(self): + def test_end_to_end_trace(self): + from appbuilder.utils.trace.tracer import AppBuilderTracer + tracer=AppBuilderTracer( + enable_phoenix = True, + enable_console = False, + ) + + tracer.start_trace() assistant = appbuilder.assistant.assistants.create( name="test_function", description="你是一个热心的朋友", @@ -85,6 +92,30 @@ def test_end_to_end(self): ) as stream: stream.until_done() + tracer.end_trace() + + def test_end_to_end(self): + assistant = appbuilder.assistant.assistants.create( + name="test_function", + description="你是一个热心的朋友", + instructions="请用友善的语气回答问题", + tools=[ + {'type': 'function', 'function': check_tool} + ] + ) + + thread = appbuilder.assistant.threads.create() + appbuilder.assistant.threads.messages.create( + thread_id=thread.id, + content="今天北京的天气怎么样?", + ) + with appbuilder.assistant.threads.runs.stream_run_with_handler( + thread_id=thread.id, + assistant_id=assistant.id, + event_handler=MyEventHandler(), + ) as stream: + stream.until_done() + if __name__ == "__main__": unittest.main() diff --git a/appbuilder/utils/model_util.py b/appbuilder/utils/model_util.py index a1cd942a1..2b0c83113 100644 --- a/appbuilder/utils/model_util.py +++ b/appbuilder/utils/model_util.py @@ -18,6 +18,7 @@ import appbuilder from appbuilder.core._client import HTTPClient +from appbuilder.utils.trace.tracer_wrapper import list_trace r"""模型名称到简称的映射. """ @@ -298,6 +299,7 @@ def __init__(self, """ self.http_client = client or HTTPClient(secret_key, gateway) + @list_trace def list(self, request: GetModelListRequest = None, timeout: float = None, retry: int = 0) -> GetModelListResponse: """ diff --git a/appbuilder/utils/trace/_function.py b/appbuilder/utils/trace/_function.py index 4df54a585..f3b639b34 100644 --- a/appbuilder/utils/trace/_function.py +++ b/appbuilder/utils/trace/_function.py @@ -15,14 +15,37 @@ import json import inspect from typing import Generator +from pydantic import BaseModel +from appbuilder import Message def _time(start_time,end_time,span): + """ + 设置时间跨度属性为两个时间点的差值(秒)。 + + Args: + start_time (float): 开始时间的时间戳(秒为单位)。 + end_time (float): 结束时间的时间戳(秒为单位)。 + span (object): OpenTelemetry的Span对象,用于设置时间跨度属性。 + + Returns: + None: 该函数不返回任何值,直接在传入的span对象上设置属性。 + + """ span.set_attribute('time.cost-time',str(end_time-start_time)+'s') - def _build_curl_from_post(url, headers, json_body, timeout) -> str: """ - Generate cURL command from post request parameters. + 从 POST 请求参数生成 cURL 命令。 + + Args: + url (str): 请求的 URL 地址。 + headers (dict): 请求头信息,以字典形式传入。 + json_body (dict): JSON 格式的请求体数据,以字典形式传入。 + timeout (int, optional): 请求的超时时间(秒)。默认为 None,表示不设置超时时间。 + + Returns: + str: 生成的 cURL 命令字符串。 + """ curl = f"curl -L '{url}' \\\n" header_lines = [f"-H '{k}: {v}' \\" for k, v in headers.items() if k != 'Content-Length'] @@ -41,9 +64,26 @@ def _build_curl_from_post(url, headers, json_body, timeout) -> str: def _post_input(args,kwargs,span): - + """ + 使用 POST 请求发送数据并记录相关属性到 span 中。 + + Args: + args (tuple): 元组,其中最后一个元素是发送 POST 请求的 URL。 + kwargs (dict): 字典,包含以下可选参数: + - headers (dict): HTTP 请求头,以字典形式表示。 + - json (dict, optional): 发送的 JSON 数据体,默认为 None。 + - timeout (int, optional): 请求超时时间(秒),默认为 None。 + span (object): OpenTelemetry 的 span 对象,用于记录相关属性。 + + Returns: + None: 该函数没有返回值,主要用于发送 POST 请求并记录相关信息。 + + """ + url = kwargs.get('url',None) + if not url: + url = args[-1] curl=_build_curl_from_post( - url=args[-1], + url=url, headers=kwargs['headers'], json_body=kwargs.get('json',None), timeout=kwargs.get('timeout',None), @@ -51,7 +91,22 @@ def _post_input(args,kwargs,span): span.set_attribute("input.value","{}".format(curl)) -def _client_input(args,kwargs,span): +def _input(args,kwargs,span): + """ + 将函数参数转化为字符串形式并存储在字典中,然后将字典转化为JSON字符串并设置为span的属性。 + + Args: + args (tuple): 函数的位置参数元组。 + kwargs (dict): 函数的关键字参数字典。 + span (opentracing.Span): 用于记录日志的OpenTracing span对象。 + + Returns: + None: 此函数没有返回值,主要用于设置span的属性。 + + Raises: + 无特定异常抛出,但如果在处理参数或设置span属性时发生异常,将打印异常信息。 + + """ input_dict={} type_name = (bool,str,bytes,int,float,list,dict) sig = inspect.signature(args[0]) @@ -61,17 +116,32 @@ def _client_input(args,kwargs,span): for idx, value in enumerate(list(args)): if isinstance(value, type_name): input_dict[list(params)[idx-1]] = str(value) - if kwargs: + elif isinstance(value, Message): + input_dict[list(params)[idx-1]] = str(value) for key, value in dict(kwargs).items(): if isinstance(value, type_name): + input_dict[key] = value + elif isinstance(value, Message): input_dict[key] = str(value) - - span.set_attribute("input.value",json.dumps(input_dict, ensure_ascii=False)) + if input_dict: + span.set_attribute("input.value",json.dumps(input_dict, ensure_ascii=False)) except Exception as e: print(e) def _client_run_trace_output(output,span,tracer): + """ + 根据给定的输出、span和tracer,记录并处理客户端的trace输出信息。 + + Args: + output (Any): 客户端的输出信息,可能是一个生成器或AppBuilderClientAnswer对象。 + span (Span): Jaeger的Span对象,用于记录trace信息。 + tracer (Tracer): Jaeger的Tracer对象,用于创建新的Span。 + + Returns: + list: 如果output是生成器类型,则返回生成器中的消息列表;否则返回空列表。 + + """ if output: run_list = [] generator_list = [] @@ -101,7 +171,7 @@ def _client_run_trace_output(output,span,tracer): new_span.set_attribute("output.value", "{}".format(message.model_dump_json(indent=4))) except Exception as e: print(e) - result += message.answer + result += str(message.answer) if hasattr(message, 'events') and message.events and hasattr(message.events[0], 'event_type') and hasattr(message.events[0], 'status'): run_list.append('{}[status:{}]'.format(message.events[0].event_type,message.events[0].status)) @@ -138,12 +208,36 @@ def _client_run_trace_output(output,span,tracer): return generator_list def _return_generator(run_list) -> Generator: + """ + 返回一个生成器,逐个生成并返回run_list中的元素。 + + Args: + run_list (list): 包含要生成的元素的列表。 + + Returns: + Generator: 返回一个生成器,用于逐个生成并返回run_list中的元素。 + + """ for item in run_list: yield item def _client_tool_trace_output_deep_iterate(output,span): + """ + 对客户端工具的输出进行深度迭代,并设置OpenTelemetry的span属性。 + + Args: + output (Union[dict, bool, str, bytes, int, float, list]): 客户端工具的输出结果,可以是字典、布尔值、字符串、字节串、整数、浮点数或列表。 + span (Span): OpenTelemetry的span对象,用于设置span属性。 + + Returns: + None: 此函数不返回任何值,但会设置span的"output.value"属性。 + + Raises: + 无特定异常,但会捕获并打印所有在函数执行过程中发生的异常。 + + """ input_dict={} - type_name = (bool,str,bytes,int,float,list,dict) + type_name = (bool,str,bytes,int,float,list) try: if isinstance(output, dict): for key, value in dict(output).items(): @@ -157,15 +251,42 @@ def _client_tool_trace_output_deep_iterate(output,span): print(e) -def _client_tool_trace_output(output, span): - if inspect.isclass(output): - output_dict = output.__dict__ - _client_tool_trace_output_deep_iterate(output=output_dict,span=span) - else: +def _output(output, span): + """ + 将输出值转换为字符串并设置到span的属性中 + + Args: + output (Any): 待输出的值,可以是任意类型,但如果是BaseModel或其子类或类对象,将使用model_dump_json方法进行序列化 + span (Span): Jaeger的Span对象,用于记录跟踪信息 + + Returns: + None: 此函数不返回任何值,但会将输出值转换为字符串并设置到span的属性中 + + Raises: + 无: 此函数不引发任何异常 + + """ + type_name = (bool,str,bytes,int,float,list,dict) + if isinstance(output, BaseModel) or inspect.isclass(output): + span.set_attribute("output.value", "{}".format(output.model_dump_json(indent=4))) + elif isinstance(output, type_name): _client_tool_trace_output_deep_iterate(output=output,span=span) def _tool_name(args): + """ + 根据传入的函数或方法对象,返回其工具名称。 + + Args: + args (tuple): 包含单个元素的元组,该元素为函数或方法对象。 + + Returns: + str: 返回字符串,表示函数或方法的工具名称。 + + - 如果函数或方法是类的实例方法,则返回形如"类名-方法名"的字符串。 + - 如果函数或方法不是类的实例方法,则直接返回函数或方法名。 + + """ class_name = args[0].__qualname__.split('.')[0] function_name = args[0].__name__ @@ -175,7 +296,168 @@ def _tool_name(args): return "{}-{}".format(class_name,function_name) +def _assistant_output(output, span): + """ + 设置span的属性,将output的模型转储为JSON格式的字符串并赋值给span的output.value属性。 + + Args: + output (Any): 任意类型的数据,预期是包含模型转储功能的对象。 + span (Span): 用于设置属性的span对象。 + + Returns: + None: 该函数不返回任何值,而是通过修改span对象来实现功能。 + + """ + span.set_attribute("output.value", "{}".format(output.model_dump_json(indent=4))) + +def _assistant_stream_output(output, span, tracer): + """ + 处理流式输出,并生成追踪信息。 + + Args: + output (Iterator[Any]): 流式输出数据的迭代器。 + span (Span): 追踪信息的Span对象。 + tracer (Tracer): 追踪信息的Tracer对象。 + + Returns: + List[Any]: 存储所有输出消息的列表。 + + """ + result = '' + run_list = [] + generator_list = [] + if output: + new_span = tracer.start_span('Assistant-Stream_run') + for message in output: + generator_list.append(message) + new_span.set_attribute("openinference.span.kind",'agent') + if message.event == "status": + new_span.set_attribute("output.value", "{}".format(message.model_dump_json(indent=4))) + elif message.event == "message": + new_span.set_attribute("output.value", "{}".format(message.model_dump_json(indent=4))) + if hasattr(message, 'content') and message.content and hasattr(message.content[0], 'text') and message.content[0].text and hasattr(message.content[0].text, 'value'): + run_list.append(message.content[0].text.value) + new_span.end() + new_span = tracer.start_span('Client-Stream') + for item in run_list: + result += str(item) + new_span.set_attribute("output.value",'流式输出结束\n输出结果为:{}'.format(result)) + new_span.set_attribute("openinference.span.kind",'agent') + new_span.end() + span.set_attribute("output.value",result) + return generator_list + +def _assistant_stream_run_with_handler_output(output,span,tracer): + """ + 处理带有事件处理器的输出流,并生成相关的span和输出列表。 + + Args: + output (Any): 事件处理器的输出对象,包含_event_handler属性。 + span (Span): 追踪的span对象。 + tracer (Tracer): 追踪器对象。 + + Returns: + None: 该函数没有返回值,但会修改传入的output对象的_event_handler._iterator属性。 + + """ + result = '' + output_list = [] + generator_list = [] + if output: + new_span = tracer.start_span('Assistant-Stream_run_with_handler') + if hasattr(output, '_event_handler') and output._event_handler: + event_handler = output._event_handler + if hasattr(event_handler, '_iterator') and event_handler._iterator: + for message in event_handler._iterator: + generator_list.append(message) + new_span.set_attribute("openinference.span.kind",'agent') + if isinstance(message, BaseModel): + new_span.set_attribute("output.value", "{}".format(message.model_dump_json(indent=4))) + else: + new_span.set_attribute("output.value", "{}".format(json.dumps(message, ensure_ascii=False))) + if hasattr(message, 'content') and message.content and message.content[0]: + if hasattr(message.content[0], 'text') and message.content[0].text: + if hasattr(message.content[0].text, 'value') and message.content[0].text.value: + output_list.append(message.content[0].text.value) + new_span.end() + new_span = tracer.start_span('Assistant-Stream_run_with_handler') + new_span.set_attribute("output.value",'流式运行结束') + new_span.set_attribute("openinference.span.kind",'agent') + new_span.end() + for item in output_list: + result += str(item) + span.set_attribute("output.value", result) + output._event_handler._iterator = _return_generator(generator_list) + + return output + +def _components_run_output(output, span): + """ + 设置span的属性以记录输出信息。 + + Args: + output (Any): 运行组件后得到的输出对象,需要包含 `token_usage` 属性(如果存在)。 + span (Span): Jaeger中的span对象,用于记录追踪信息。 + + Returns: + None: 此函数不返回任何值,而是直接修改span对象的属性。 + + """ + if hasattr(output, 'token_usage') and output.token_usage: + span.set_attribute("llm.token_count.prompt", output.token_usage.get('prompt_tokens', 0)) + span.set_attribute("llm.token_count.completion", output.token_usage.get('completion_tokens', 0)) + span.set_attribute("llm.token_count.total", output.token_usage.get('total_tokens', 0)) + span.set_attribute("output.value", "{}".format(output.model_dump_json(indent=4))) + +def _components_stream_output(output, span, tracer): + """ + 将组件的输出流转换为字符串并返回生成器列表。 + + Args: + output (Iterable[Any]): 组件的输出流,可以是任何可迭代对象。 + span (opentelemetry.trace.Span): 当前追踪的span对象。 + tracer (opentelemetry.trace.Tracer): 追踪器对象,用于创建span。 + + Returns: + List[Any]: 组件输出流的生成器列表。 + + """ + result = '' + run_list = [] + generator_list = [] + if output: + for message in output: + with tracer.start_as_current_span('Component-Stream') as new_span: + new_span.set_attribute("openinference.span.kind",'agent') + generator_list.append(message) + new_span.set_attribute("openinference.span.kind",'tool') + new_span.set_attribute("output.value", "{}".format(json.dumps(message, ensure_ascii=False))) + if isinstance(message, dict): + run_list.append(message.get('text', None)) + else: + try: + run_list.append(str(message)) + except: + print("message can't to be str") + for item in run_list: + result += str(item) + span.set_attribute("output.value",result) + return generator_list + def _post_trace(tracer, func, *args, **kwargs): + """ + 对指定的HTTP POST请求函数进行追踪,并生成追踪信息。 + + Args: + tracer (Any): 追踪器实例,用于开始和结束追踪。 + func (Callable[..., Any]): 需要被追踪的HTTP POST请求函数。 + *args: 可变位置参数,用于传递给func函数。 + **kwargs: 可变关键字参数,用于传递给func函数。 + + Returns: + Any: func函数的返回值。 + + """ url = args[-1] if not isinstance(url, str): url = kwargs.get('url','') @@ -190,16 +472,28 @@ def _post_trace(tracer, func, *args, **kwargs): return result def _client_run_trace(tracer, func, *args, **kwargs): + """ + 跟踪客户端运行函数,并添加跟踪信息。 + + Args: + tracer (Any): 跟踪器对象,用于生成和追踪span。 + func (Callable[..., Any]): 需要跟踪的函数。 + *args: 可变位置参数,func函数的输入参数。 + **kwargs: 可变关键字参数,func函数的输入参数。 + + Returns: + Any: func函数的返回结果,若函数返回结果是generator类型,则将其转换为列表后返回。 + + """ with tracer.start_as_current_span('AppBuilderClient-RUN') as new_span: start_time = time.time() result=func(*args, **kwargs) end_time = time.time() _time(start_time = start_time,end_time = end_time,span = new_span) new_span.set_attribute("openinference.span.kind",'Agent') - _client_input(args = args, kwargs = kwargs, span=new_span) + _input(args = args, kwargs = kwargs, span=new_span) generator_list = _client_run_trace_output(output=result,span = new_span,tracer=tracer) - if generator_list: result.content = _return_generator(generator_list) return result @@ -207,12 +501,202 @@ def _client_run_trace(tracer, func, *args, **kwargs): return result def _client_tool_trace(tracer, func, *args, **kwargs): + """ + 追踪客户端工具函数的调用,记录相关信息到追踪器。 + + Args: + tracer (Any): 追踪器实例,用于创建和记录追踪信息。 + func (Callable[..., Any]): 要追踪的客户端工具函数。 + *args (Any): 传递给func的位置参数。 + **kwargs (Any): 传递给func的关键字参数。 + + Returns: + Any: func函数执行后的返回值。 + + """ with tracer.start_as_current_span(_tool_name(args=args)) as new_span: start_time = time.time() result=func(*args, **kwargs) end_time = time.time() _time(start_time = start_time,end_time = end_time,span = new_span) new_span.set_attribute("openinference.span.kind",'tool') - _client_tool_trace_output(output=result, span = new_span) + _output(output=result, span = new_span) + return result + +def _assistant_tool_trace(tracer, func, *args, **kwargs): + """ + 对给定的函数进行追踪,记录其执行的时间、输入和输出。 + + Args: + tracer: OpenTelemetry的Tracer实例,用于创建span。 + func: 需要追踪的函数。 + *args: 传递给func的位置参数。 + **kwargs: 传递给func的关键字参数。 + + Returns: + func的返回值。 + + """ + span_name = _tool_name(args=args) + with tracer.start_as_current_span(span_name) as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + _time(start_time = start_time,end_time = end_time,span = new_span) + _input(args = args, kwargs = kwargs, span=new_span) + new_span.set_attribute("openinference.span.kind",'tool') + if result: + _output(output=result, span = new_span) return result + +def _assistant_run_trace(tracer, func, *args, **kwargs): + """ + 使用给定的追踪器(tracer)对函数(func)的执行进行追踪。 + + Args: + tracer (object): 追踪器对象,用于创建和操作追踪的span。 + func (callable): 需要被追踪的函数。 + *args (tuple): 传递给func的位置参数。 + **kwargs (dict): 传递给func的关键字参数。 + + Returns: + Any: 被追踪函数func的返回值。 + + """ + with tracer.start_as_current_span("Assistant-RUN") as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + _time(start_time = start_time,end_time = end_time,span = new_span) + new_span.set_attribute("openinference.span.kind",'Agent') + _input(args = args, kwargs = kwargs, span=new_span) + _assistant_output(output=result, span = new_span) + return result + +def _assistant_stream_trace(tracer, func, *args, **kwargs): + """ + 为给定函数func添加分布式追踪功能,记录函数执行时间、参数、返回值等信息,并生成对应的追踪span。 + + Args: + tracer (Tracer): 分布式追踪器对象,用于生成span。 + func (Callable[..., Any]): 要被追踪的函数。 + *args: 函数func的位置参数。 + **kwargs: 函数func的关键字参数。 + + Returns: + Any: 函数func的返回值,如果func返回的是生成器类型,则返回一个封装了生成器的对象。 + + """ + with tracer.start_as_current_span("Assistant-stream_run") as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + _time(start_time = start_time,end_time = end_time,span = new_span) + new_span.set_attribute("openinference.span.kind",'Agent') + _input(args = args, kwargs = kwargs, span=new_span) + generator_list = _assistant_stream_output(output=result, span = new_span, tracer=tracer) + if generator_list: + result = _return_generator(generator_list) + return result + +def _assistant_stream_run_with_handler_trace(tracer, func, *args, **kwargs): + """ + 为给定函数func添加分布式追踪功能,记录函数执行时间、参数、返回值等信息,并生成对应的追踪span。 + + Args: + tracer (Tracer): 分布式追踪器对象,用于生成span。 + func (Callable[..., Any]): 要被追踪的函数。 + *args: 函数func的位置参数。 + **kwargs: 函数func的关键字参数。 + + Returns: + Any: 函数func的返回值,如果func返回的是生成器类型,则返回一个封装了生成器的对象。 + + """ + with tracer.start_as_current_span("Assistant-stream_run_with_handler") as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + _time(start_time = start_time,end_time = end_time,span = new_span) + new_span.set_attribute("openinference.span.kind",'Agent') + _input(args = args, kwargs = kwargs, span=new_span) + result = _assistant_stream_run_with_handler_output(output=result, span = new_span, tracer=tracer) + return result + +def _components_run_trace(tracer, func, *args, **kwargs): + """ + 追踪组件执行的函数装饰器。 + + Args: + tracer (Tracer): 追踪器对象,用于创建和管理跟踪的span。 + func (Callable[..., Any]): 要追踪的函数。 + *args: 可变数量的位置参数,传递给func。 + **kwargs: 可变数量的关键字参数,传递给func。 + + Returns: + Any: func函数执行后的结果。 + + """ + + with tracer.start_as_current_span(_tool_name(args=args)) as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + _time(start_time = start_time,end_time = end_time,span = new_span) + new_span.set_attribute("openinference.span.kind",'tool') + _input(args = args, kwargs = kwargs, span=new_span) + _components_run_output(output=result, span = new_span) + + return result + +def _components_stream_run_trace(tracer, func, *args, **kwargs): + """ + 跟踪组件流执行过程的装饰器函数。 + + Args: + tracer (Tracer): 追踪器对象,用于创建和操作追踪的span。 + func (Callable): 需要被追踪的函数。 + *args: 可变参数列表,传入func的参数。 + **kwargs: 关键字参数列表,传入func的参数。 + + Returns: + Any: func函数执行后的返回值,如果返回值是生成器类型,则会被转换成迭代器类型。 + + """ + with tracer.start_as_current_span(_tool_name(args=args)) as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + _time(start_time = start_time,end_time = end_time,span = new_span) + new_span.set_attribute("openinference.span.kind",'tool') + _input(args = args, kwargs = kwargs, span=new_span) + generator_list = _components_stream_output(output=result, span = new_span, tracer=tracer) + if generator_list: + result = _return_generator(generator_list) + return result + +def _list_trace(tracer, func, *args, **kwargs): + """ + 使用给定的tracer对函数func进行追踪,并记录相关信息到span中。 + + Args: + tracer (OpenTelemetryTracer): 用于追踪的tracer对象。 + func (Callable[..., Any]): 需要被追踪的函数。 + *args: 传递给func的位置参数。 + **kwargs: 传递给func的关键字参数。 + + Returns: + Any: 调用func的返回结果。 + + """ + with tracer.start_as_current_span(_tool_name(args = args)) as new_span: + start_time = time.time() + result=func(*args, **kwargs) + end_time = time.time() + new_span.set_attribute("openinference.span.kind",'tool') + new_span.set_attribute("input.value",_tool_name(args = args)) + new_span.set_attribute("output.value",str(result)) + return result + + \ No newline at end of file diff --git a/appbuilder/utils/trace/phoenix_wrapper.py b/appbuilder/utils/trace/phoenix_wrapper.py index cfc3e9d15..d43eb485d 100644 --- a/appbuilder/utils/trace/phoenix_wrapper.py +++ b/appbuilder/utils/trace/phoenix_wrapper.py @@ -30,8 +30,8 @@ def launch_phoenix(host: Optional[str] = "127.0.0.1", port: Optional[int] = 8080 """ try: import phoenix - except ImportError: - raise ( + except ModuleNotFoundError: + raise ImportError( "尚未安装phoenix组件,尝试使用 python3 -m pip install arize-phoenix==4.5.0 -i https://pypi.tuna.tsinghua.edu.cn/simple 安装该组件") session = phoenix.launch_app(**kwargs) return session @@ -43,8 +43,8 @@ def stop_phoenix(delete_data:bool=False): """ try: import phoenix - except ImportError: - raise ( + except ModuleNotFoundError: + raise ImportError( "尚未安装phoenix组件,尝试使用 python3 -m pip install arize-phoenix==4.5.0 -i https://pypi.tuna.tsinghua.edu.cn/simple 安装该组件") phoenix.stop_app(delete_data) @@ -59,16 +59,13 @@ def runtime_main(): timeout = args.timeout print(" Launching AppBuilder Tracer Server By Phoenix... ") print(" Arguments: ", args) - try: - session = launch_phoenix() - while True: - if timeout is not None: - time.sleep(timeout) - break - time.sleep(1) - stop_phoenix() - except Exception as e: - print(e) + session = launch_phoenix() + while True: + if timeout is not None: + time.sleep(timeout) + break + time.sleep(1) + stop_phoenix() if __name__ == "__main__": diff --git a/appbuilder/utils/trace/tracer.py b/appbuilder/utils/trace/tracer.py index 919385261..e15cf248c 100644 --- a/appbuilder/utils/trace/tracer.py +++ b/appbuilder/utils/trace/tracer.py @@ -25,7 +25,18 @@ ) from wrapt import wrap_function_wrapper -from appbuilder.utils.trace._function import _post_trace, _client_run_trace, _client_tool_trace +from appbuilder.utils.trace._function import( + _post_trace, + _client_run_trace, + _client_tool_trace, + _assistant_tool_trace, + _assistant_run_trace, + _assistant_stream_trace, + _assistant_stream_run_with_handler_trace, + _components_run_trace, + _components_stream_run_trace, + _list_trace, + ) from appbuilder import logger _MODULE_1 = 'appbuilder' @@ -37,15 +48,48 @@ class AppbuilderInstrumentor(BaseInstrumentor): Instrumentor for appbuilder and appbuilder-sdk-ext. """ + _instance = None + _instrumented = False + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance + __slots__ = ( "_original_session_post", '_original_client_run', '_original_client_tool', + '_original_assistant_tool', + '_original_assistant_run', + '_original_assistant_stream_run', + '_original_assistant_stream_run_with_handler', + '_orignal_components_run', + '_original_components_stream_run', + '_original_list', ) def instrumentation_dependencies(self): pass def _instrument(self, **kwargs): + """ + 为指定模块的函数添加跟踪功能。 + + Args: + **kwargs: 可选参数,用于提供跟踪器提供程序。 + + Returns: + None + + Raises: + ImportError: 如果从文件中缺少`run_trace`、`tool_eval_streaming_trace`和`assistant_trace`方法,则引发此异常。 + Exception: 如果未找到`appbuilder`和`appbuilder-sdk-ext`模块,则引发此异常。 + + """ + if self._instrumented: + return + self._instrumented = True + if not (tracer_provider := kwargs.get("tracer_provider")): tracer_provider = trace.get_tracer_provider() @@ -57,10 +101,28 @@ def _instrument(self, **kwargs): # 保存原始函数的引用 try: - from .tracer_wrapper import session_post_func, client_run_trace_func, client_tool_trace_func + from .tracer_wrapper import ( + session_post_func, + client_run_trace_func, + client_tool_trace_func, + assistent_tool_trace_func, + assistant_run_trace_func, + assistent_stream_run_trace_func, + assistant_stream_run_with_handler_trace_func, + components_run_trace_func, + components_run_stream_trace_func, + list_trace_func, + ) self._original_session_post = session_post_func self._original_client_run = client_run_trace_func self._original_client_tool = client_tool_trace_func + self._original_assistant_tool = assistent_tool_trace_func + self._original_assistant_run = assistant_run_trace_func + self._original_assistant_stream_run = assistent_stream_run_trace_func + self._original_assistant_stream_run_with_handler = assistant_stream_run_with_handler_trace_func + self._orignal_components_run = components_run_trace_func + self._original_components_stream_run = components_run_stream_trace_func + self._original_list = list_trace_func except: raise ImportError( "Please check if the run_trace, tool_eval_streaming_trace, and assistant_trace methods are missing from the file.") @@ -71,10 +133,6 @@ def _instrument(self, **kwargs): except: appbuilder = None - try: - appbuilder_sdk_ext = import_module(_MODULE_2) - except: - appbuilder_sdk_ext = None def _appbuilder_session_post(wrapped, instance, args, kwargs): return _post_trace(tracer, self._original_session_post, *args, **kwargs) @@ -85,6 +143,27 @@ def _appbuilder_client_run_trace(wrapped, instance, args, kwargs): def _appbuilder_client_tool_trace(wrapped, instance, args, kwargs): return _client_tool_trace(tracer, self._original_client_tool, *args, **kwargs) + def _appbuilder_assistant_tool_trace(wrapped, instance, args, kwargs): + return _assistant_tool_trace(tracer, self._original_assistant_tool, *args, **kwargs) + + def _appbuilder_assistant_run_trace(wrapped, instance, args, kwargs): + return _assistant_run_trace(tracer, self._original_assistant_run, *args, **kwargs) + + def _appbuilder_assistant_stream_run_trace(wrapped, instance, args, kwargs): + return _assistant_stream_trace(tracer, self._original_assistant_stream_run, *args, **kwargs) + + def _appbuilder_assistant_stream_run_with_handler_trace(wrapped, instance, args, kwargs): + return _assistant_stream_run_with_handler_trace(tracer, self._original_assistant_stream_run_with_handler, *args, **kwargs) + + def _appbuilder_components_run_trace(wrapped, instance, args, kwargs): + return _components_run_trace(tracer, self._orignal_components_run, *args, **kwargs) + + def _appbuilder_components_run_stream_trace(wrapped, instance, args, kwargs): + return _components_stream_run_trace(tracer, self._original_components_stream_run, *args, **kwargs) + + def _appbuilder_list_trace(wrapped, instance, args, kwargs): + return _list_trace(tracer, self._original_list, *args, **kwargs) + # 引用相关函数并替换 if appbuilder: @@ -106,21 +185,107 @@ def _appbuilder_client_tool_trace(wrapped, instance, args, kwargs): wrapper = _appbuilder_client_tool_trace ) - if not appbuilder_sdk_ext and not appbuilder: - raise Exception("appbuilder and appbuilder-sdk-ext not found") + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.assistent_tool_trace_func', + wrapper= _appbuilder_assistant_tool_trace + ) + + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.assistant_run_trace_func', + wrapper= _appbuilder_assistant_run_trace + ) + + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.assistent_stream_run_trace_func', + wrapper= _appbuilder_assistant_stream_run_trace + ) + + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.assistant_stream_run_with_handler_trace_func', + wrapper= _appbuilder_assistant_stream_run_with_handler_trace + ) + + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.components_run_trace_func', + wrapper= _appbuilder_components_run_trace + ) + + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.components_run_stream_trace_func', + wrapper= _appbuilder_components_run_stream_trace + ) + + wrap_function_wrapper( + module= _MODULE_1, + name = 'utils.trace.tracer_wrapper.list_trace_func', + wrapper= _appbuilder_list_trace + ) + + if not appbuilder: + raise Exception("appbuilder not found") def _uninstrument(self): + """ + 恢复原始函数,移除之前添加的追踪代码。 + + Args: + 无参数。 + + Returns: + 无返回值。 + + Raises: + 无异常抛出,但如果在尝试恢复原始函数时遇到任何问题,将打印一条错误消息"appbuilder not found"。 + + """ # 恢复原始函数 try: - from appbuilder.utils.trace.tracer_wrapper import session_post_func, client_run_trace_func, client_tool_trace_func + from appbuilder.utils.trace.tracer_wrapper import ( + session_post_func, + client_run_trace_func, + client_tool_trace_func, + assistent_tool_trace_func, + assistant_run_trace_func, + assistant_stream_run_with_handler_trace_func, + components_run_trace_func, + components_run_stream_trace_func, + list_trace_func, + ) + session_post_func = self._original_session_post client_run_trace_func = self._original_client_run client_tool_trace_func = self._original_client_tool + assistent_tool_trace_func = self._original_assistant_tool + assistant_run_trace_func = self._original_assistant_run + assistant_stream_run_with_handler_trace_func = self._original_assistant_stream_run_with_handler + components_run_trace_func = self._orignal_components_run + components_run_stream_trace_func = self._original_components_stream_run + list_trace_func = self._original_list except: print("appbuilder not found") def create_tracer_provider(enable_phoenix: bool = True, enable_console: bool = False, host: str = "127.0.0.1", port: int = 8080, method: str = "/v1/traces"): + """ + 创建一个用于跟踪的TracerProvider对象,并可选择性地添加span处理器,以便将跟踪数据发送到指定的端点或控制台。 + + Args: + enable_phoenix (bool, optional): 是否启用Phoenix,以在本地可视化界面展示trace数据。默认为True。 + enable_console (bool, optional): 是否启用控制台输出,以在控制台展示trace数据。默认为False。 + host (str, optional): Phoenix可视化界面的主机地址。默认为"127.0.0.1"。 + port (int, optional): Phoenix可视化界面的端口号。默认为8080。 + method (str, optional): Phoenix可视化界面的请求路径。默认为"/v1/traces"。 + + Returns: + TracerProvider: 创建的TracerProvider对象,可用于创建跟踪的Span对象。 + + """ tracer_provider = TracerProvider() if enable_phoenix: # 将trace数据在本地可视化界面展示 @@ -137,7 +302,29 @@ def create_tracer_provider(enable_phoenix: bool = True, enable_console: bool = F class AppBuilderTracer(): + _instance = None + _trace_start = False + + def __new__(cls, *args, **kwargs): + if cls._instance is None: + cls._instance = super().__new__(cls) + return cls._instance + def __init__(self, enable_phoenix: bool = True, enable_console: bool = False, host: str = "http://localhost", port: int = 8080, method="/v1/traces") -> None: + """ + 初始化函数,用于设置追踪系统相关参数。 + + Args: + enable_phoenix (bool, optional): 是否启用Phoenix服务。默认为True。 + enable_console (bool, optional): 是否启用控制台输出。默认为False。 + host (str, optional): 可视化追踪系统服务的地址。默认为"http://localhost"。 + port (int, optional): 可视化追踪系统服务的端口号。默认为8080。 + method (str, optional): 可视化追踪系统服务的方法路径。默认为"/v1/traces"。 + + Returns: + None: 无返回值。 + + """ self._tracer_provider = create_tracer_provider( enable_phoenix=enable_phoenix, enable_console=enable_console, @@ -159,6 +346,8 @@ def add_custom_processor(self, processor): self._tracer_provider.add_span_processor(processor) def start_trace(self): + if self._trace_start: + return logger.info("AppBuilder Starting trace...") self._instrumentor._instrument(tracer_provider=self._tracer_provider) diff --git a/appbuilder/utils/trace/tracer_wrapper.py b/appbuilder/utils/trace/tracer_wrapper.py index daa4aecf5..173cff235 100644 --- a/appbuilder/utils/trace/tracer_wrapper.py +++ b/appbuilder/utils/trace/tracer_wrapper.py @@ -24,8 +24,38 @@ def client_run_trace_func(func, *args, **kwargs): def client_tool_trace_func(func, *args, **kwargs): return func(*args, **kwargs) +def assistent_tool_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) + +def assistant_run_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) + +def assistant_stream_run_with_handler_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) + +def assistent_stream_run_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) + +def components_run_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) + +def components_run_stream_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) + +def list_trace_func(func, *args, **kwargs): + return func(*args, **kwargs) def session_post(func): + """ + 对给定的函数添加 session post 请求装饰器 + + Args: + func (callable): 需要被装饰的函数 + + Returns: + callable: 返回一个装饰器函数,该函数在被调用时会使用 session post 请求执行原始函数 + + """ @wraps(func) def wrapper(*args, **kwargs): return session_post_func(func, *args, **kwargs) @@ -34,6 +64,16 @@ def wrapper(*args, **kwargs): def client_run_trace(func): + """ + 为一个函数添加追踪功能,记录函数调用的开始和结束时间,以及函数的输入参数和返回结果。 + + Args: + func (callable): 需要被追踪的函数。 + + Returns: + callable: 封装后的函数,该函数在被调用时会执行原函数,并添加追踪功能。 + + """ @wraps(func) def wrapper(*args, **kwargs): @@ -44,9 +84,141 @@ def wrapper(*args, **kwargs): def client_tool_trace(func): + """ + 装饰器函数,用于跟踪客户端工具函数的调用情况。 + + Args: + func (callable): 需要被跟踪的函数。 + + Returns: + callable: 返回一个装饰器函数,该函数会调用原函数并记录相关信息。 + + """ @wraps(func) def wrapper(*args, **kwargs): return client_tool_trace_func(func, *args, **kwargs) return wrapper + + +def assistent_tool_trace(func): + """ + 用于辅助追踪函数执行情况的装饰器。 + + Args: + func (Callable[..., Any]): 需要被装饰的函数,接受任意数量和类型的参数。 + + Returns: + Callable[..., Any]: 返回一个函数,该函数会在调用原函数前后记录一些信息, + 然后将原函数的返回值返回。 + + """ + @wraps(func) + def wrapper(*args, **kwargs): + return assistent_tool_trace_func(func, *args, **kwargs) + + return wrapper + + +def assistant_run_trace(func): + """ + 对函数进行装饰,用于在函数执行前后进行日志跟踪。 + + Args: + func (Callable): 需要进行日志跟踪的函数。 + + Returns: + Callable: 经过装饰后,带有日志跟踪功能的函数。 + + """ + @wraps(func) + def wrapper(*args, **kwargs): + return assistant_run_trace_func(func, *args, **kwargs) + + return wrapper + +def assistent_stream_run_trace(func): + """ + 对目标函数进行包装,以启用辅助流执行追踪功能。 + + Args: + func (Callable): 需要包装的目标函数,必须是一个可调用的对象。 + + Returns: + Callable: 包装后的函数对象,调用时将执行辅助流执行追踪,并返回目标函数的执行结果。 + + """ + @wraps(func) + def wrapper(*args, **kwargs): + return assistent_stream_run_trace_func(func, *args, **kwargs) + + return wrapper + +def assistent_stream_run_with_handler_trace(func): + """ + 为函数添加助手流运行和处理器跟踪的装饰器。 + + Args: + func (Callable): 需要被装饰的函数,即助手流运行的入口函数。 + + Returns: + Callable: 返回一个包装后的函数,该函数在调用时会执行assistant_stream_run_with_handler_trace_func函数, + 并将原始函数func及其参数传递给它。 + + """ + @wraps(func) + def wrapper(*args, **kwargs): + return assistant_stream_run_with_handler_trace_func(func, *args, **kwargs) + + return wrapper + +def components_run_trace(func): + """ + 为函数添加组件运行跟踪的装饰器。 + + Args: + func (Callable[..., Any]): 需要添加跟踪的函数。 + + Returns: + Callable[..., Any]: 装饰后的函数,当被调用时,会调用 components_run_trace_func 并传入原始函数和参数。 + + """ + @wraps(func) + def wrapper(*args, **kwargs): + return components_run_trace_func(func, *args, **kwargs) + + return wrapper + +def components_run_stream_trace(func): + """ + 为给定的函数添加流追踪功能,用于追踪函数内部组件的运行情况。 + + Args: + func (callable): 需要添加流追踪功能的函数。 + + Returns: + callable: 返回一个装饰器函数,当被装饰的函数被调用时,会执行流追踪功能。 + + """ + @wraps(func) + def wrapper(*args, **kwargs): + return components_run_stream_trace_func(func, *args, **kwargs) + + return wrapper + +def list_trace(func): + """ + 为函数添加列表追踪的装饰器。 + + Args: + func (Callable[..., Any]): 需要被装饰的函数,接受任意数量和类型的参数。 + Returns: + Callable[..., Any]: 返回一个装饰器函数,该函数在被调用时会执行原始函数并记录相关信息。 + """ + @wraps(func) + def wrapper(*args, **kwargs): + return list_trace_func(func, *args, **kwargs) + + return wrapper + diff --git a/cookbooks/README.md b/cookbooks/README.md index ca246feac..9fbc3633b 100644 --- a/cookbooks/README.md +++ b/cookbooks/README.md @@ -32,3 +32,6 @@ ### 进阶应用 - [公有云部署](/cookbooks/advanced_application/cloud_deploy.ipynb) + +### 辅助工具 +- [Appbuilder-Trace工具](/cookbooks/appbuilder_trace/trace.ipynb) diff --git a/cookbooks/appbuilder_trace/trace_client.ipynb b/cookbooks/appbuilder_trace/trace.ipynb similarity index 99% rename from cookbooks/appbuilder_trace/trace_client.ipynb rename to cookbooks/appbuilder_trace/trace.ipynb index e0c5eb3a5..69a3a8dd0 100644 --- a/cookbooks/appbuilder_trace/trace_client.ipynb +++ b/cookbooks/appbuilder_trace/trace.ipynb @@ -67,7 +67,7 @@ } ], "source": [ - "from appbuilder.utils.trace.tracer import AppBuilderTracer\n", + "from appbuilder import AppBuilderTracer\n", "tracer=AppBuilderTracer(\n", " enable_phoenix = True,\n", " enable_console = False,\n", diff --git a/docs/README.md b/docs/README.md index 23dfaf883..6d2f7a7eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -17,6 +17,9 @@ - [基础能力组件](https://github.com/baidubce/app-builder/blob/master/docs/basic_module/components.md) - [进阶实践](https://github.com/baidubce/app-builder/blob/master/docs/advanced_application/README.md) - [CookBooks](https://github.com/baidubce/app-builder/blob/master/cookbooks/README.md) + - [Appbuilder-SDK Trace跟踪功能](https://github.com/baidubce/app-builder/blob/master/docs/trace/READED.md) + - [Appbuilder-SDK Trace基础用法](https://github.com/baidubce/app-builder/blob/master/docs/trace/basic.md) + - [Appbuilder-SDK Trace进阶用法](https://github.com/baidubce/app-builder/blob/master/docs/trace/phoenix_method.md) - [SDK服务化部署](https://github.com/baidubce/app-builder/blob/master/docs/service/README.md) - [API调用](https://github.com/baidubce/app-builder/blob/master/docs/service/flask.md) - [交互式服务](https://github.com/baidubce/app-builder/blob/master/docs/service/chainlit.md) diff --git a/docs/trace/README.md b/docs/trace/README.md new file mode 100644 index 000000000..34be7755f --- /dev/null +++ b/docs/trace/README.md @@ -0,0 +1,6 @@ +# Appbuilder Trace 文档 + +本文档目录包含以下内容 + +- [Appbuilder Trace跟踪功能基本用法](./basic.md) +- [Phoneix可视化软件的进阶用法](./phoenix_method.md) \ No newline at end of file diff --git a/appbuilder/utils/trace/README.md b/docs/trace/basic.md similarity index 73% rename from appbuilder/utils/trace/README.md rename to docs/trace/basic.md index 8bddd150c..e625198c3 100644 --- a/appbuilder/utils/trace/README.md +++ b/docs/trace/basic.md @@ -23,7 +23,7 @@ appbuilder_trace_server ### 启动Appbuilder-SDK TRACE ```python -from appbuilder.utils.trace.tracer import AppBuilderTracer +from appbuilder import AppBuilderTracer tracer=AppBuilderTracer( enable_phoenix = True, enable_console = False, @@ -52,4 +52,8 @@ for m in msg.content: # 结束trace tracer.end_trace() -``` \ No newline at end of file +``` + +- Phoenix可视化效果 + +![Phoenix可视化效果](https://bj.bcebos.com/v1/appbuilder-sdk-components/Phoenix%E5%8F%AF%E8%A7%86%E5%8C%96%E7%95%8C%E9%9D%A2%EF%BC%883%EF%BC%89.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-07-03T03%3A19%3A12Z%2F-1%2Fhost%2Fe79bcd6c9edbae95c98d789464621986fbb74b2f5a10936a555a1fe89f435624) \ No newline at end of file diff --git a/docs/trace/phoenix_method.md b/docs/trace/phoenix_method.md new file mode 100644 index 000000000..4d9d78d4a --- /dev/null +++ b/docs/trace/phoenix_method.md @@ -0,0 +1,33 @@ +# Phoneix可视化软件的进阶用法 + +## 1. 选择可视化展示的选项 + +- 点击Columns,选择需要可视化展示的字段 + +![选择可视化展示的选项](https://bj.bcebos.com/v1/appbuilder-sdk-components/%E4%BF%AE%E6%94%B9phoenix%E5%B1%95%E7%A4%BA%E5%8F%82%E6%95%B0.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-07-11T07%3A15%3A16Z%2F-1%2Fhost%2Ffef2c48927c21fb9ab285e26989ed650d622200957c02da021b108c5aa60db9f) + + +## 2. 查看节点关键信息 + +- 点击需要查看的节点,查看节点关键信息,Info将展示input、ouput信息 + +![查看节点关键信息 ](https://bj.bcebos.com/v1/appbuilder-sdk-components/Phoenix%E5%8F%AF%E8%A7%86%E5%8C%96%E7%95%8C%E9%9D%A2%EF%BC%883%EF%BC%89.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-07-03T03%3A19%3A12Z%2F-1%2Fhost%2Fe79bcd6c9edbae95c98d789464621986fbb74b2f5a10936a555a1fe89f435624) + +## 3. 搜索目标节点 + +### 依据节点类型搜索 + +- 点击搜索框,输入节点类型,搜索目标节点(eg: span_kind == 'AGENT') +- 在Traces界面,只能检索到根节点、在Spans界面,可以检索到所有节点 + +![依据节点类型搜索](https://bj.bcebos.com/v1/appbuilder-sdk-components/span_kind_find.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-07-11T07%3A33%3A50Z%2F-1%2Fhost%2F8e05c22c7e37100c0899d7a46a5f285bf9546bd8612bf8f228d0af7a094a30bb) + +### 依据节点信息检索 + +- 依据节点信息检索可快速定位到目标节点 + +![依据节点信息检索](https://bj.bcebos.com/v1/appbuilder-sdk-components/span_value_find.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-07-11T07%3A39%3A42Z%2F-1%2Fhost%2F3032eb48087793090bc8f29c7c202b59f10135db10ebbbcab424b34270304d87) + +### 其他多种检索方式 + +![其他多种检索方式](https://bj.bcebos.com/v1/appbuilder-sdk-components/every_span_find.png?authorization=bce-auth-v1%2FALTAKGa8m4qCUasgoljdEDAzLm%2F2024-07-11T07%3A43%3A27Z%2F-1%2Fhost%2F591db7e50adffb2171d20cdc1b27e7f6d11e8255acb66c76fd5c614b73683a77) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index e6dc422cd..190a0d288 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ requests proto-plus==1.22.3 pydantic==2.6.4 numpy -SQLAlchemy +SQLAlchemy==2.0.31 urllib3<2.0.0 tenacity==8.2.3 pandas