langtrace-python-sdk 2.3.2__py3-none-any.whl → 2.3.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. examples/anthropic_example/completion.py +1 -1
  2. examples/crewai_example/instagram_post/__init__.py +0 -0
  3. examples/crewai_example/instagram_post/agents.py +96 -0
  4. examples/crewai_example/instagram_post/main.py +80 -0
  5. examples/crewai_example/instagram_post/tasks.py +146 -0
  6. examples/crewai_example/instagram_post/tools/__init__.py +0 -0
  7. examples/crewai_example/instagram_post/tools/browser_tools.py +40 -0
  8. examples/openai_example/__init__.py +1 -0
  9. langtrace_python_sdk/instrumentation/anthropic/instrumentation.py +10 -9
  10. langtrace_python_sdk/instrumentation/anthropic/patch.py +33 -29
  11. langtrace_python_sdk/instrumentation/anthropic/types.py +105 -0
  12. langtrace_python_sdk/instrumentation/cohere/patch.py +1 -4
  13. langtrace_python_sdk/instrumentation/crewai/instrumentation.py +15 -0
  14. langtrace_python_sdk/instrumentation/crewai/patch.py +47 -25
  15. langtrace_python_sdk/instrumentation/gemini/patch.py +2 -5
  16. langtrace_python_sdk/instrumentation/groq/patch.py +7 -19
  17. langtrace_python_sdk/instrumentation/openai/instrumentation.py +14 -19
  18. langtrace_python_sdk/instrumentation/openai/patch.py +93 -101
  19. langtrace_python_sdk/instrumentation/openai/types.py +170 -0
  20. langtrace_python_sdk/instrumentation/vertexai/patch.py +2 -5
  21. langtrace_python_sdk/instrumentation/weaviate/patch.py +3 -13
  22. langtrace_python_sdk/langtrace.py +20 -21
  23. langtrace_python_sdk/utils/llm.py +12 -7
  24. langtrace_python_sdk/utils/silently_fail.py +19 -3
  25. langtrace_python_sdk/version.py +1 -1
  26. {langtrace_python_sdk-2.3.2.dist-info → langtrace_python_sdk-2.3.4.dist-info}/METADATA +1 -1
  27. {langtrace_python_sdk-2.3.2.dist-info → langtrace_python_sdk-2.3.4.dist-info}/RECORD +30 -22
  28. {langtrace_python_sdk-2.3.2.dist-info → langtrace_python_sdk-2.3.4.dist-info}/WHEEL +0 -0
  29. {langtrace_python_sdk-2.3.2.dist-info → langtrace_python_sdk-2.3.4.dist-info}/entry_points.txt +0 -0
  30. {langtrace_python_sdk-2.3.2.dist-info → langtrace_python_sdk-2.3.4.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,7 @@ from langtrace_python_sdk import langtrace, with_langtrace_root_span
7
7
 
8
8
  _ = load_dotenv(find_dotenv())
9
9
 
10
- langtrace.init()
10
+ langtrace.init(write_spans_to_console=True)
11
11
 
12
12
 
13
13
  @with_langtrace_root_span("messages_create")
File without changes
@@ -0,0 +1,96 @@
1
+ from crewai import Agent
2
+ from tools.browser_tools import scrape_and_summarize_website
3
+ from langchain_openai import ChatOpenAI
4
+ # from langchain.llms import Ollama
5
+
6
+
7
+ class MarketingAnalysisAgents:
8
+ def __init__(self):
9
+ # self.llm = Ollama(model=os.environ["MODEL"])
10
+ self.llm = ChatOpenAI(model_name="gpt-4", temperature=0.7)
11
+
12
+ def product_competitor_agent(self):
13
+ return Agent(
14
+ role="Lead Market Analyst",
15
+ goal="""\
16
+ Conduct amazing analysis of the products and
17
+ competitors, providing in-depth insights to guide
18
+ marketing strategies.""",
19
+ backstory="""\
20
+ As the Lead Market Analyst at a premier
21
+ digital marketing firm, you specialize in dissecting
22
+ online business landscapes.""",
23
+ tools=[scrape_and_summarize_website],
24
+ allow_delegation=False,
25
+ llm=self.llm,
26
+ verbose=True,
27
+ )
28
+
29
+ def strategy_planner_agent(self):
30
+ return Agent(
31
+ role="Chief Marketing Strategist",
32
+ goal="""\
33
+ Synthesize amazing insights from product analysis
34
+ to formulate incredible marketing strategies.""",
35
+ backstory="""\
36
+ You are the Chief Marketing Strategist at
37
+ a leading digital marketing agency, known for crafting
38
+ bespoke strategies that drive success.""",
39
+ tools=[scrape_and_summarize_website],
40
+ llm=self.llm,
41
+ verbose=True,
42
+ )
43
+
44
+ def creative_content_creator_agent(self):
45
+ return Agent(
46
+ role="Creative Content Creator",
47
+ goal="""\
48
+ Develop compelling and innovative content
49
+ for social media campaigns, with a focus on creating
50
+ high-impact Instagram ad copies.""",
51
+ backstory="""\
52
+ As a Creative Content Creator at a top-tier
53
+ digital marketing agency, you excel in crafting narratives
54
+ that resonate with audiences on social media.
55
+ Your expertise lies in turning marketing strategies
56
+ into engaging stories and visual content that capture
57
+ attention and inspire action.""",
58
+ tools=[scrape_and_summarize_website],
59
+ llm=self.llm,
60
+ verbose=True,
61
+ )
62
+
63
+ def senior_photographer_agent(self):
64
+ return Agent(
65
+ role="Senior Photographer",
66
+ goal="""\
67
+ Take the most amazing photographs for instagram ads that
68
+ capture emotions and convey a compelling message.""",
69
+ backstory="""\
70
+ As a Senior Photographer at a leading digital marketing
71
+ agency, you are an expert at taking amazing photographs that
72
+ inspire and engage, you're now working on a new campaign for a super
73
+ important customer and you need to take the most amazing photograph.""",
74
+ tools=[scrape_and_summarize_website],
75
+ llm=self.llm,
76
+ allow_delegation=False,
77
+ verbose=True,
78
+ )
79
+
80
+ def chief_creative_diretor_agent(self):
81
+ return Agent(
82
+ role="Chief Creative Director",
83
+ goal="""\
84
+ Oversee the work done by your team to make sure it's the best
85
+ possible and aligned with the product's goals, review, approve,
86
+ ask clarifying question or delegate follow up work if necessary to make
87
+ decisions""",
88
+ backstory="""\
89
+ You're the Chief Content Officer of leading digital
90
+ marketing specialized in product branding. You're working on a new
91
+ customer, trying to make sure your team is crafting the best possible
92
+ content for the customer.""",
93
+ tools=[scrape_and_summarize_website],
94
+ llm=self.llm,
95
+ verbose=True,
96
+ )
@@ -0,0 +1,80 @@
1
+
2
+ from dotenv import load_dotenv
3
+
4
+ load_dotenv()
5
+
6
+
7
+ from langtrace_python_sdk import langtrace
8
+
9
+ langtrace.init()
10
+
11
+ from crewai import Crew
12
+ from agents import MarketingAnalysisAgents
13
+ from tasks import MarketingAnalysisTasks
14
+
15
+ tasks = MarketingAnalysisTasks()
16
+ agents = MarketingAnalysisAgents()
17
+
18
+ print("## Welcome to the marketing Crew")
19
+ print('-------------------------------')
20
+ product_website = "https://langtrace.ai"
21
+ product_details = "LLM App monitoring"
22
+
23
+
24
+ # Create Agents
25
+ product_competitor_agent = agents.product_competitor_agent()
26
+ # strategy_planner_agent = agents.strategy_planner_agent()
27
+ # creative_agent = agents.creative_content_creator_agent()
28
+ # # Create Tasks
29
+ website_analysis = tasks.product_analysis(product_competitor_agent, product_website, product_details)
30
+ # market_analysis = tasks.competitor_analysis(product_competitor_agent, product_website, product_details)
31
+ # campaign_development = tasks.campaign_development(strategy_planner_agent, product_website, product_details)
32
+ # write_copy = tasks.instagram_ad_copy(creative_agent)
33
+
34
+ # Create Crew responsible for Copy
35
+ copy_crew = Crew(
36
+ agents=[
37
+ product_competitor_agent,
38
+ # strategy_planner_agent,
39
+ # creative_agent
40
+ ],
41
+ tasks=[
42
+ website_analysis,
43
+ # market_analysis,
44
+ # campaign_development,
45
+ # write_copy
46
+ ],
47
+ verbose=True
48
+ )
49
+
50
+ ad_copy = copy_crew.kickoff()
51
+
52
+ # Create Crew responsible for Image
53
+ # senior_photographer = agents.senior_photographer_agent()
54
+ # chief_creative_diretor = agents.chief_creative_diretor_agent()
55
+ # # Create Tasks for Image
56
+ # take_photo = tasks.take_photograph_task(senior_photographer, ad_copy, product_website, product_details)
57
+ # approve_photo = tasks.review_photo(chief_creative_diretor, product_website, product_details)
58
+
59
+ # image_crew = Crew(
60
+ # agents=[
61
+ # senior_photographer,
62
+ # chief_creative_diretor
63
+ # ],
64
+ # tasks=[
65
+ # take_photo,
66
+ # approve_photo
67
+ # ],
68
+ # verbose=True
69
+ # )
70
+
71
+ # image = image_crew.kickoff()
72
+
73
+ # Print results
74
+ print("\n\n########################")
75
+ print("## Here is the result")
76
+ print("########################\n")
77
+ print("Your post copy:")
78
+ print(ad_copy)
79
+ # print("'\n\nYour midjourney description:")
80
+ # print(image)
@@ -0,0 +1,146 @@
1
+ from crewai import Task
2
+ from textwrap import dedent
3
+
4
+
5
+ class MarketingAnalysisTasks:
6
+ def product_analysis(self, agent, product_website, product_details):
7
+ return Task(
8
+ description=dedent(
9
+ f"""\
10
+ Analyze the given product website: {product_website}.
11
+ Extra details provided by the customer: {product_details}.
12
+
13
+ Focus on identifying unique features, benefits,
14
+ and the overall narrative presented.
15
+
16
+ Your final report should clearly articulate the
17
+ product's key selling points, its market appeal,
18
+ and suggestions for enhancement or positioning.
19
+ Emphasize the aspects that make the product stand out.
20
+
21
+ Keep in mind, attention to detail is crucial for
22
+ a comprehensive analysis. It's currenlty 2024.
23
+ """
24
+ ),
25
+ agent=agent,
26
+ expected_output="A detailed analysis of the product website, highlighting key features, benefits, and market positioning.",
27
+ )
28
+
29
+ def competitor_analysis(self, agent, product_website, product_details):
30
+ return Task(
31
+ description=dedent(
32
+ f"""\
33
+ Explore competitor of: {product_website}.
34
+ Extra details provided by the customer: {product_details}.
35
+
36
+ Identify the top 3 competitors and analyze their
37
+ strategies, market positioning, and customer perception.
38
+
39
+ Your final report MUST include BOTH all context about {product_website}
40
+ and a detailed comparison to whatever competitor they have competitors.
41
+ """
42
+ ),
43
+ agent=agent,
44
+ expected_output="A comprehensive analysis of the top 3 competitors, including their strategies, market positioning, and customer perception.",
45
+ )
46
+
47
+ def campaign_development(self, agent, product_website, product_details):
48
+ return Task(
49
+ description=dedent(
50
+ f"""\
51
+ You're creating a targeted marketing campaign for: {product_website}.
52
+ Extra details provided by the customer: {product_details}.
53
+
54
+ To start this campaing we will need a strategy and creative content ideas.
55
+ It should be meticulously designed to captivate and engage
56
+ the product's target audience.
57
+
58
+ Based on your ideas your co-workers will create the content for the campaign.
59
+
60
+ Your final answer MUST be ideas that will resonate with the audience and
61
+ also include ALL context you have about the product and the customer.
62
+ """
63
+ ),
64
+ agent=agent,
65
+ expected_output="A detailed marketing campaign strategy, including creative content ideas and target audience analysis.",
66
+ )
67
+
68
+ def instagram_ad_copy(self, agent):
69
+ return Task(
70
+ description=dedent(
71
+ """\
72
+ Craft an engaging Instagram post copy.
73
+ The copy should be punchy, captivating, concise,
74
+ and aligned with the product marketing strategy.
75
+
76
+ Focus on creating a message that resonates with
77
+ the target audience and highlights the product's
78
+ unique selling points.
79
+
80
+ Your ad copy must be attention-grabbing and should
81
+ encourage viewers to take action, whether it's
82
+ visiting the website, making a purchase, or learning
83
+ more about the product.
84
+
85
+ Your final answer MUST be 3 options for an ad copy for instagram that
86
+ not only informs but also excites and persuades the audience.
87
+ """
88
+ ),
89
+ agent=agent,
90
+ expected_output="Three engaging Instagram ad copies that align with the product marketing strategy.",
91
+ )
92
+
93
+ def take_photograph_task(self, agent, copy, product_website, product_details):
94
+ return Task(
95
+ description=dedent(
96
+ f"""\
97
+ You are working on a new campaign for a super important customer,
98
+ and you MUST take the most amazing photo ever for an instagram post
99
+ regarding the product, you have the following copy:
100
+ {copy}
101
+
102
+ This is the product you are working with: {product_website}.
103
+ Extra details provided by the customer: {product_details}.
104
+
105
+ Imagine what the photo you wanna take describe it in a paragraph.
106
+ Here are some examples for you follow:
107
+ - high tech airplaine in a beautiful blue sky in a beautiful sunset super cripsy beautiful 4k, professional wide shot
108
+ - the last supper, with Jesus and his disciples, breaking bread, close shot, soft lighting, 4k, crisp
109
+ - an bearded old man in the snows, using very warm clothing, with mountains full of snow behind him, soft lighting, 4k, crisp, close up to the camera
110
+
111
+ Think creatively and focus on how the image can capture the audience's
112
+ attention. Don't show the actual product on the photo.
113
+
114
+ Your final answer must be 3 options of photographs, each with 1 paragraph
115
+ describing the photograph exactly like the examples provided above.
116
+ """
117
+ ),
118
+ agent=agent,
119
+ expected_output="Three high-quality photographs that creatively capture the essence of the product and engage the audience.",
120
+ )
121
+
122
+ def review_photo(self, agent, product_website, product_details):
123
+ return Task(
124
+ description=dedent(
125
+ f"""\
126
+ Review the photos you got from the senior photographer.
127
+ Make sure it's the best possible and aligned with the product's goals,
128
+ review, approve, ask clarifying question or delegate follow up work if
129
+ necessary to make decisions. When delegating work send the full draft
130
+ as part of the information.
131
+
132
+ This is the product you are working with: {product_website}.
133
+ Extra details provided by the customer: {product_details}.
134
+
135
+ Here are some examples on how the final photographs should look like:
136
+ - high tech airplaine in a beautiful blue sky in a beautiful sunset super cripsy beautiful 4k, professional wide shot
137
+ - the last supper, with Jesus and his disciples, breaking bread, close shot, soft lighting, 4k, crisp
138
+ - an bearded old man in the snows, using very warm clothing, with mountains full of snow behind him, soft lighting, 4k, crisp, close up to the camera
139
+
140
+ Your final answer must be 3 reviewed options of photographs,
141
+ each with 1 paragraph description following the examples provided above.
142
+ """
143
+ ),
144
+ agent=agent,
145
+ expected_output="Three reviewed photographs that align with the product's goals and meet the required standards.",
146
+ )
@@ -0,0 +1,40 @@
1
+ import json
2
+ import os
3
+
4
+ import requests
5
+ from crewai import Agent, Task
6
+ from crewai_tools import tool
7
+ from unstructured.partition.html import partition_html
8
+
9
+ from langchain_openai import ChatOpenAI
10
+
11
+
12
+ @tool
13
+ def scrape_and_summarize_website(website):
14
+ """Useful to scrape and summarize a website content, just pass a string with
15
+ only the full url, no need for a final slash `/`, eg: https://google.com or https://clearbit.com/about-us"""
16
+ url = f"https://chrome.browserless.io/content?token={os.environ['BROWSERLESS_API_KEY']}"
17
+ payload = json.dumps({"url": website})
18
+ headers = {'cache-control': 'no-cache', 'content-type': 'application/json'}
19
+ response = requests.request("POST", url, headers=headers, data=payload)
20
+ elements = partition_html(text=response.text)
21
+ content = "\n\n".join([str(el) for el in elements])
22
+ content = [content[i:i + 8000] for i in range(0, len(content), 8000)]
23
+ summaries = []
24
+ for chunk in content:
25
+ agent = Agent(
26
+ role='Principal Researcher',
27
+ goal='Do amazing researches and summaries based on the content you are working with',
28
+ backstory="You're a Principal Researcher at a big company and you need to do a research about a given topic.",
29
+ # llm=Ollama(model=os.environ['MODEL']),
30
+ llm=ChatOpenAI(model_name="gpt-4", temperature=0.7),
31
+ allow_delegation=False)
32
+ task = Task(
33
+ agent=agent,
34
+ description=f'Analyze and make a LONG summary the content bellow, make sure to include the ALL relevant information in the summary, return only the summary nothing else.\n\nCONTENT\n----------\n{chunk}',
35
+ expected_output='A long summary of the content',
36
+ )
37
+ summary = task.execute_sync()
38
+ summaries.append(summary)
39
+ content = "\n\n".join(summaries)
40
+ return f'\nScrapped Content: {content}\n'
@@ -20,3 +20,4 @@ class OpenAIRunner:
20
20
  chat_completion_example()
21
21
  embeddings_create_example()
22
22
  function_example()
23
+ image_edit()
@@ -16,38 +16,39 @@ limitations under the License.
16
16
 
17
17
  import importlib.metadata
18
18
  import logging
19
- from typing import Collection
19
+ from typing import Collection, Any
20
20
 
21
21
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
22
+ from opentelemetry.trace import TracerProvider
22
23
  from opentelemetry.trace import get_tracer
23
24
  from wrapt import wrap_function_wrapper
24
-
25
+ from typing import Any
25
26
  from langtrace_python_sdk.instrumentation.anthropic.patch import messages_create
26
27
 
27
28
  logging.basicConfig(level=logging.FATAL)
28
29
 
29
30
 
30
- class AnthropicInstrumentation(BaseInstrumentor):
31
+ class AnthropicInstrumentation(BaseInstrumentor): # type: ignore[misc]
31
32
  """
32
- The AnthropicInstrumentation class represents the Anthropic instrumentation
33
+ The AnthropicInstrumentation class represents the Anthropic instrumentation.
33
34
  """
34
35
 
35
36
  def instrumentation_dependencies(self) -> Collection[str]:
36
37
  return ["anthropic >= 0.19.1"]
37
38
 
38
- def _instrument(self, **kwargs):
39
- tracer_provider = kwargs.get("tracer_provider")
39
+ def _instrument(self, **kwargs: dict[str, Any]) -> None:
40
+ tracer_provider: TracerProvider = kwargs.get("tracer_provider") # type: ignore
40
41
  tracer = get_tracer(__name__, "", tracer_provider)
41
42
  version = importlib.metadata.version("anthropic")
42
43
 
43
44
  wrap_function_wrapper(
44
45
  "anthropic.resources.messages",
45
46
  "Messages.create",
46
- messages_create("anthropic.messages.create", version, tracer),
47
+ messages_create(version, tracer),
47
48
  )
48
49
 
49
- def _instrument_module(self, module_name):
50
+ def _instrument_module(self, module_name: str) -> None:
50
51
  pass
51
52
 
52
- def _uninstrument(self, **kwargs):
53
+ def _uninstrument(self, **kwargs: dict[str, Any]) -> None:
53
54
  pass
@@ -14,9 +14,8 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  """
16
16
 
17
- import json
18
-
19
- from langtrace.trace_attributes import Event, LLMSpanAttributes
17
+ from typing import Any, Callable, Dict, List, Optional, Iterator, TypedDict, Union
18
+ from langtrace.trace_attributes import Event, SpanAttributes, LLMSpanAttributes
20
19
  from langtrace_python_sdk.utils import set_span_attribute
21
20
  from langtrace_python_sdk.utils.silently_fail import silently_fail
22
21
 
@@ -27,41 +26,48 @@ from langtrace_python_sdk.utils.llm import (
27
26
  get_llm_request_attributes,
28
27
  get_llm_url,
29
28
  get_span_name,
30
- is_streaming,
31
29
  set_event_completion,
32
30
  set_usage_attributes,
31
+ set_span_attribute,
33
32
  )
34
- from opentelemetry.trace import SpanKind
35
- from opentelemetry.trace.status import Status, StatusCode
36
- from langtrace.trace_attributes import SpanAttributes
37
-
33
+ from opentelemetry.trace import Span, Tracer, SpanKind
34
+ from opentelemetry.trace.status import StatusCode
38
35
  from langtrace_python_sdk.constants.instrumentation.anthropic import APIS
39
- from langtrace_python_sdk.constants.instrumentation.common import (
40
- SERVICE_PROVIDERS,
36
+ from langtrace_python_sdk.constants.instrumentation.common import SERVICE_PROVIDERS
37
+ from langtrace_python_sdk.instrumentation.anthropic.types import (
38
+ StreamingResult,
39
+ ResultType,
40
+ MessagesCreateKwargs,
41
+ ContentItem,
42
+ Usage,
41
43
  )
42
44
 
43
45
 
44
- def messages_create(original_method, version, tracer):
46
+ def messages_create(version: str, tracer: Tracer) -> Callable[..., Any]:
45
47
  """Wrap the `messages_create` method."""
46
48
 
47
- def traced_method(wrapped, instance, args, kwargs):
49
+ def traced_method(
50
+ wrapped: Callable[..., Any],
51
+ instance: Any,
52
+ args: List[Any],
53
+ kwargs: MessagesCreateKwargs,
54
+ ) -> Any:
48
55
  service_provider = SERVICE_PROVIDERS["ANTHROPIC"]
49
56
 
50
- # extract system from kwargs and attach as a role to the prompts
51
- # we do this to keep it consistent with the openai
57
+ # Extract system from kwargs and attach as a role to the prompts
52
58
  prompts = kwargs.get("messages", [])
53
59
  system = kwargs.get("system")
54
60
  if system:
55
61
  prompts = [{"role": "system", "content": system}] + kwargs.get(
56
62
  "messages", []
57
63
  )
58
-
64
+ extraAttributes = get_extra_attributes()
59
65
  span_attributes = {
60
66
  **get_langtrace_attributes(version, service_provider),
61
67
  **get_llm_request_attributes(kwargs, prompts=prompts),
62
68
  **get_llm_url(instance),
63
69
  SpanAttributes.LLM_PATH: APIS["MESSAGES_CREATE"]["ENDPOINT"],
64
- **get_extra_attributes(),
70
+ **extraAttributes, # type: ignore
65
71
  }
66
72
 
67
73
  attributes = LLMSpanAttributes(**span_attributes)
@@ -74,37 +80,35 @@ def messages_create(original_method, version, tracer):
74
80
  try:
75
81
  # Attempt to call the original method
76
82
  result = wrapped(*args, **kwargs)
77
- return set_response_attributes(result, span, kwargs)
83
+ return set_response_attributes(result, span)
78
84
 
79
85
  except Exception as err:
80
86
  # Record the exception in the span
81
87
  span.record_exception(err)
82
88
  # Set the span status to indicate an error
83
- span.set_status(Status(StatusCode.ERROR, str(err)))
89
+ span.set_status(StatusCode.ERROR, str(err))
84
90
  # Reraise the exception to ensure it's not swallowed
85
91
  span.end()
86
92
  raise
87
93
 
88
- @silently_fail
89
- def set_response_attributes(result, span, kwargs):
90
- if not is_streaming(kwargs):
94
+ def set_response_attributes(
95
+ result: Union[ResultType, StreamingResult], span: Span
96
+ ) -> Any:
97
+ if not isinstance(result, Iterator):
91
98
  if hasattr(result, "content") and result.content is not None:
92
99
  set_span_attribute(
93
100
  span, SpanAttributes.LLM_RESPONSE_MODEL, result.model
94
101
  )
102
+ content_item = result.content[0]
95
103
  completion = [
96
104
  {
97
- "role": result.role if result.role else "assistant",
98
- "content": result.content[0].text,
99
- "type": result.content[0].type,
105
+ "role": result.role or "assistant",
106
+ "content": content_item.text,
107
+ "type": content_item.type,
100
108
  }
101
109
  ]
102
110
  set_event_completion(span, completion)
103
111
 
104
- else:
105
- responses = []
106
- set_event_completion(span, responses)
107
-
108
112
  if (
109
113
  hasattr(result, "system_fingerprint")
110
114
  and result.system_fingerprint is not None
@@ -116,7 +120,7 @@ def messages_create(original_method, version, tracer):
116
120
  # Get the usage
117
121
  if hasattr(result, "usage") and result.usage is not None:
118
122
  usage = result.usage
119
- set_usage_attributes(span, dict(usage))
123
+ set_usage_attributes(span, vars(usage))
120
124
 
121
125
  span.set_status(StatusCode.OK)
122
126
  span.end()
@@ -0,0 +1,105 @@
1
+ """
2
+ Copyright (c) 2024 Scale3 Labs
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+ http://www.apache.org/licenses/LICENSE-2.0
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
12
+ """
13
+
14
+ from typing import Dict, List, Optional, Iterator, TypedDict
15
+
16
+
17
+ class MessagesCreateKwargs(TypedDict, total=False):
18
+ system: Optional[str]
19
+ messages: List[Dict[str, str]]
20
+
21
+
22
+ class Usage:
23
+ input_tokens: int
24
+ output_tokens: int
25
+
26
+ def __init__(self, input_tokens: int, output_tokens: int):
27
+ self.input_tokens = input_tokens
28
+ self.output_tokens = output_tokens
29
+
30
+
31
+ class Message:
32
+ def __init__(
33
+ self,
34
+ id: str,
35
+ model: Optional[str],
36
+ usage: Optional[Usage],
37
+ ):
38
+ self.id = id
39
+ self.model = model
40
+ self.usage = usage
41
+
42
+ model: Optional[str]
43
+ usage: Optional[Usage]
44
+
45
+
46
+ class Delta:
47
+ text: Optional[str]
48
+
49
+ def __init__(
50
+ self,
51
+ text: Optional[str],
52
+ ):
53
+ self.text = text
54
+
55
+
56
+ class Chunk:
57
+ message: Message
58
+ delta: Delta
59
+
60
+ def __init__(
61
+ self,
62
+ message: Message,
63
+ delta: Delta,
64
+ ):
65
+ self.message = message
66
+ self.delta = delta
67
+
68
+
69
+ class ContentItem:
70
+ role: str
71
+ content: str
72
+ text: str
73
+ type: str
74
+
75
+ def __init__(self, role: str, content: str, text: str, type: str):
76
+ self.role = role
77
+ self.content = content
78
+ self.text = text
79
+ self.type = type
80
+
81
+
82
+ class ResultType:
83
+ model: Optional[str]
84
+ role: Optional[str]
85
+ content: List[ContentItem]
86
+ system_fingerprint: Optional[str]
87
+ usage: Optional[Usage]
88
+
89
+ def __init__(
90
+ self,
91
+ model: Optional[str],
92
+ role: Optional[str],
93
+ content: Optional[List[ContentItem]],
94
+ system_fingerprint: Optional[str],
95
+ usage: Optional[Usage],
96
+ ):
97
+ self.model = model
98
+ self.role = role
99
+ self.content = content if content is not None else []
100
+ self.system_fingerprint = system_fingerprint
101
+ self.usage = usage
102
+
103
+
104
+ # The result would be an iterator that yields these Chunk objects
105
+ StreamingResult = Iterator[Chunk]
@@ -367,10 +367,7 @@ def chat_stream(original_method, version, tracer):
367
367
  }
368
368
  for item in chat_history
369
369
  ]
370
- if len(history) > 0:
371
- prompts = history + prompts
372
- if len(system_prompts) > 0:
373
- prompts = system_prompts + prompts
370
+ prompts = system_prompts + history + prompts
374
371
 
375
372
  span_attributes = {
376
373
  **get_langtrace_attributes(version, service_provider),