langtrace-python-sdk 2.3.1__py3-none-any.whl → 2.3.3__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.
- examples/crewai_example/instagram_post/__init__.py +0 -0
- examples/crewai_example/instagram_post/agents.py +96 -0
- examples/crewai_example/instagram_post/main.py +80 -0
- examples/crewai_example/instagram_post/tasks.py +146 -0
- examples/crewai_example/instagram_post/tools/__init__.py +0 -0
- examples/crewai_example/instagram_post/tools/browser_tools.py +40 -0
- langtrace_python_sdk/extensions/langtrace_exporter.py +47 -9
- langtrace_python_sdk/instrumentation/crewai/instrumentation.py +15 -0
- langtrace_python_sdk/instrumentation/crewai/patch.py +37 -16
- langtrace_python_sdk/langtrace.py +30 -23
- langtrace_python_sdk/version.py +1 -1
- {langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/METADATA +1 -1
- {langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/RECORD +16 -10
- {langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/WHEEL +0 -0
- {langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/entry_points.txt +0 -0
- {langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/licenses/LICENSE +0 -0
|
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
|
+
)
|
|
File without changes
|
|
@@ -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'
|
|
@@ -76,7 +76,29 @@ class LangTraceExporter(SpanExporter):
|
|
|
76
76
|
Returns:
|
|
77
77
|
The result of the export SUCCESS or FAILURE
|
|
78
78
|
"""
|
|
79
|
-
|
|
79
|
+
headers = {
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
"x-api-key": self.api_key,
|
|
82
|
+
"User-Agent": "LangtraceExporter",
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Check if the OTEL_EXPORTER_OTLP_HEADERS environment variable is set
|
|
86
|
+
otel_headers = os.getenv("OTEL_EXPORTER_OTLP_HEADERS", None)
|
|
87
|
+
if otel_headers:
|
|
88
|
+
# Parse and add to headers
|
|
89
|
+
for header in otel_headers.split(","):
|
|
90
|
+
key, value = header.split("=")
|
|
91
|
+
headers[key.strip()] = value.strip()
|
|
92
|
+
|
|
93
|
+
# Check if the OTEL_EXPORTER_OTLP_TRACES_HEADERS environment variable is set
|
|
94
|
+
otel_traces_headers = os.getenv("OTEL_EXPORTER_OTLP_TRACES_HEADERS", None)
|
|
95
|
+
if otel_traces_headers:
|
|
96
|
+
# Parse and add to headers
|
|
97
|
+
for header in otel_traces_headers.split(","):
|
|
98
|
+
key, value = header.split("=")
|
|
99
|
+
headers[key.strip()] = value.strip()
|
|
100
|
+
|
|
101
|
+
if not headers["x-api-key"] and not self.disable_logging:
|
|
80
102
|
print(Fore.RED)
|
|
81
103
|
print(
|
|
82
104
|
"Missing Langtrace API key, proceed to https://langtrace.ai to create one"
|
|
@@ -101,11 +123,7 @@ class LangTraceExporter(SpanExporter):
|
|
|
101
123
|
response = requests.post(
|
|
102
124
|
url=f"{self.api_host}",
|
|
103
125
|
data=json.dumps(data),
|
|
104
|
-
headers=
|
|
105
|
-
"Content-Type": "application/json",
|
|
106
|
-
"x-api-key": self.api_key,
|
|
107
|
-
"User-Agent": "LangtraceExporter",
|
|
108
|
-
},
|
|
126
|
+
headers=headers,
|
|
109
127
|
timeout=20,
|
|
110
128
|
)
|
|
111
129
|
|
|
@@ -113,14 +131,34 @@ class LangTraceExporter(SpanExporter):
|
|
|
113
131
|
raise RequestException(response.text)
|
|
114
132
|
if not self.disable_logging:
|
|
115
133
|
print(
|
|
116
|
-
Fore.GREEN
|
|
134
|
+
Fore.GREEN
|
|
135
|
+
+ f"Exported {len(spans)} spans successfully."
|
|
136
|
+
+ Fore.RESET
|
|
117
137
|
)
|
|
118
138
|
return SpanExportResult.SUCCESS
|
|
119
139
|
except RequestException as err:
|
|
120
140
|
if not self.disable_logging:
|
|
121
141
|
print(Fore.RED + "Failed to export spans.")
|
|
122
|
-
print(Fore.RED + f"Error: {err}" + Fore.RESET)
|
|
142
|
+
print(Fore.RED + f"Error: {err}\r\n" + Fore.RESET)
|
|
143
|
+
if (
|
|
144
|
+
"invalid api key" in str(err).lower()
|
|
145
|
+
and self.api_host == f"{LANGTRACE_REMOTE_URL}/api/trace"
|
|
146
|
+
):
|
|
147
|
+
print(
|
|
148
|
+
Fore.YELLOW
|
|
149
|
+
+ "If you're self-hosting Langtrace, make sure to do one of the following to configure your trace endpoint (e.g., http://localhost:3000/api/trace):"
|
|
150
|
+
+ Fore.YELLOW
|
|
151
|
+
)
|
|
152
|
+
print(
|
|
153
|
+
Fore.YELLOW
|
|
154
|
+
+ "1. Set the `LANGTRACE_API_HOST` environment variable, or\r\n2. Pass the `api_host` parameter to the `langtrace.init()` method.\r\n"
|
|
155
|
+
+ Fore.YELLOW
|
|
156
|
+
)
|
|
123
157
|
return SpanExportResult.FAILURE
|
|
124
158
|
|
|
125
159
|
def shutdown(self) -> None:
|
|
126
|
-
print(
|
|
160
|
+
print(
|
|
161
|
+
Fore.WHITE
|
|
162
|
+
+ "⭐ Leave our github a star to stay on top of our updates - https://github.com/Scale3-Labs/langtrace"
|
|
163
|
+
+ Fore.RESET
|
|
164
|
+
)
|
|
@@ -39,6 +39,21 @@ class CrewAIInstrumentation(BaseInstrumentor):
|
|
|
39
39
|
"Crew.kickoff",
|
|
40
40
|
patch_crew("Crew.kickoff", version, tracer),
|
|
41
41
|
)
|
|
42
|
+
_W(
|
|
43
|
+
"crewai.crew",
|
|
44
|
+
"Crew.kickoff_for_each",
|
|
45
|
+
patch_crew("Crew.kickoff_for_each", version, tracer),
|
|
46
|
+
)
|
|
47
|
+
_W(
|
|
48
|
+
"crewai.crew",
|
|
49
|
+
"Crew.kickoff_async",
|
|
50
|
+
patch_crew("Crew.kickoff_async", version, tracer),
|
|
51
|
+
)
|
|
52
|
+
_W(
|
|
53
|
+
"crewai.crew",
|
|
54
|
+
"Crew.kickoff_for_each_async",
|
|
55
|
+
patch_crew("Crew.kickoff_for_each_async", version, tracer),
|
|
56
|
+
)
|
|
42
57
|
_W(
|
|
43
58
|
"crewai.agent",
|
|
44
59
|
"Agent.execute_task",
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import json
|
|
2
|
+
|
|
2
3
|
from importlib_metadata import version as v
|
|
4
|
+
from langtrace.trace_attributes import FrameworkSpanAttributes
|
|
5
|
+
from opentelemetry import baggage
|
|
6
|
+
from opentelemetry.trace import Span, SpanKind, Tracer
|
|
7
|
+
from opentelemetry.trace.status import Status, StatusCode
|
|
8
|
+
|
|
3
9
|
from langtrace_python_sdk.constants import LANGTRACE_SDK_NAME
|
|
10
|
+
from langtrace_python_sdk.constants.instrumentation.common import (
|
|
11
|
+
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY, SERVICE_PROVIDERS)
|
|
4
12
|
from langtrace_python_sdk.utils import set_span_attribute
|
|
5
13
|
from langtrace_python_sdk.utils.llm import get_span_name, set_span_attributes
|
|
6
|
-
from langtrace_python_sdk.constants.instrumentation.common import (
|
|
7
|
-
LANGTRACE_ADDITIONAL_SPAN_ATTRIBUTES_KEY,
|
|
8
|
-
SERVICE_PROVIDERS,
|
|
9
|
-
)
|
|
10
|
-
from opentelemetry import baggage
|
|
11
|
-
from langtrace.trace_attributes import FrameworkSpanAttributes
|
|
12
|
-
from opentelemetry.trace import SpanKind, Span, Tracer
|
|
13
|
-
from opentelemetry.trace.status import Status, StatusCode
|
|
14
14
|
from langtrace_python_sdk.utils.misc import serialize_args, serialize_kwargs
|
|
15
15
|
|
|
16
16
|
|
|
@@ -90,11 +90,11 @@ def patch_crew(operation_name, version, tracer: Tracer):
|
|
|
90
90
|
span.set_status(Status(StatusCode.OK))
|
|
91
91
|
if instance.__class__.__name__ == "Crew":
|
|
92
92
|
span.set_attribute("crewai.crew.result", str(result))
|
|
93
|
-
if hasattr(result, "tasks_output"):
|
|
93
|
+
if hasattr(result, "tasks_output") and result.tasks_output is not None:
|
|
94
94
|
span.set_attribute("crewai.crew.tasks_output", str((result.tasks_output)))
|
|
95
|
-
if hasattr(result, "token_usage"):
|
|
95
|
+
if hasattr(result, "token_usage") and result.token_usage is not None:
|
|
96
96
|
span.set_attribute("crewai.crew.token_usage", str((result.token_usage)))
|
|
97
|
-
if hasattr(result, "usage_metrics"):
|
|
97
|
+
if hasattr(result, "usage_metrics") and result.usage_metrics is not None:
|
|
98
98
|
span.set_attribute("crewai.crew.usage_metrics", str((result.usage_metrics)))
|
|
99
99
|
elif instance.__class__.__name__ == "Agent":
|
|
100
100
|
span.set_attribute("crewai.agent.result", str(result))
|
|
@@ -137,22 +137,27 @@ class CrewAISpanAttributes:
|
|
|
137
137
|
self.set_crew_attributes()
|
|
138
138
|
for key, value in self.crew.items():
|
|
139
139
|
key = f"crewai.crew.{key}"
|
|
140
|
-
|
|
140
|
+
if value is not None:
|
|
141
|
+
set_span_attribute(self.span, key, value)
|
|
141
142
|
|
|
142
143
|
elif instance_name == "Agent":
|
|
143
144
|
agent = self.set_agent_attributes()
|
|
144
145
|
for key, value in agent.items():
|
|
145
146
|
key = f"crewai.agent.{key}"
|
|
146
|
-
|
|
147
|
+
if value is not None:
|
|
148
|
+
set_span_attribute(self.span, key, value)
|
|
147
149
|
|
|
148
150
|
elif instance_name == "Task":
|
|
149
151
|
task = self.set_task_attributes()
|
|
150
152
|
for key, value in task.items():
|
|
151
153
|
key = f"crewai.task.{key}"
|
|
152
|
-
|
|
154
|
+
if value is not None:
|
|
155
|
+
set_span_attribute(self.span, key, value)
|
|
153
156
|
|
|
154
157
|
def set_crew_attributes(self):
|
|
155
158
|
for key, value in self.instance.__dict__.items():
|
|
159
|
+
if value is None:
|
|
160
|
+
continue
|
|
156
161
|
if key == "tasks":
|
|
157
162
|
self._parse_tasks(value)
|
|
158
163
|
elif key == "agents":
|
|
@@ -163,6 +168,8 @@ class CrewAISpanAttributes:
|
|
|
163
168
|
def set_agent_attributes(self):
|
|
164
169
|
agent = {}
|
|
165
170
|
for key, value in self.instance.__dict__.items():
|
|
171
|
+
if key == "tools":
|
|
172
|
+
value = self._parse_tools(value)
|
|
166
173
|
if value is None:
|
|
167
174
|
continue
|
|
168
175
|
agent[key] = str(value)
|
|
@@ -174,8 +181,10 @@ class CrewAISpanAttributes:
|
|
|
174
181
|
for key, value in self.instance.__dict__.items():
|
|
175
182
|
if value is None:
|
|
176
183
|
continue
|
|
177
|
-
|
|
178
|
-
|
|
184
|
+
if key == "tools":
|
|
185
|
+
value = self._parse_tools(value)
|
|
186
|
+
task[key] = value
|
|
187
|
+
elif key == "agent":
|
|
179
188
|
task[key] = value.role
|
|
180
189
|
else:
|
|
181
190
|
task[key] = str(value)
|
|
@@ -218,3 +227,15 @@ class CrewAISpanAttributes:
|
|
|
218
227
|
"output_file": task.output_file,
|
|
219
228
|
}
|
|
220
229
|
)
|
|
230
|
+
|
|
231
|
+
def _parse_tools(self, tools):
|
|
232
|
+
result = []
|
|
233
|
+
for tool in tools:
|
|
234
|
+
res = {}
|
|
235
|
+
if hasattr(tool, "name") and tool.name is not None:
|
|
236
|
+
res["name"] = tool.name
|
|
237
|
+
if hasattr(tool, "description") and tool.description is not None:
|
|
238
|
+
res["description"] = tool.description
|
|
239
|
+
if res:
|
|
240
|
+
result.append(res)
|
|
241
|
+
return json.dumps(result)
|
|
@@ -14,55 +14,54 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
17
19
|
from typing import Optional
|
|
18
20
|
|
|
19
|
-
from
|
|
20
|
-
LANGTRACE_REMOTE_URL,
|
|
21
|
-
)
|
|
22
|
-
from langtrace_python_sdk.types import (
|
|
23
|
-
DisableInstrumentations,
|
|
24
|
-
InstrumentationType,
|
|
25
|
-
InstrumentationMethods,
|
|
26
|
-
)
|
|
27
|
-
from langtrace_python_sdk.utils.langtrace_sampler import LangtraceSampler
|
|
21
|
+
from colorama import Fore
|
|
28
22
|
from opentelemetry import trace
|
|
23
|
+
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
|
|
24
|
+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
|
|
29
25
|
from opentelemetry.sdk.trace import TracerProvider
|
|
30
26
|
from opentelemetry.sdk.trace.export import (
|
|
31
27
|
BatchSpanProcessor,
|
|
32
28
|
ConsoleSpanExporter,
|
|
33
29
|
SimpleSpanProcessor,
|
|
34
30
|
)
|
|
35
|
-
import sys
|
|
36
|
-
from opentelemetry.sdk.resources import Resource, SERVICE_NAME
|
|
37
|
-
|
|
38
31
|
|
|
32
|
+
from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
|
|
33
|
+
LANGTRACE_REMOTE_URL,
|
|
34
|
+
)
|
|
39
35
|
from langtrace_python_sdk.extensions.langtrace_exporter import LangTraceExporter
|
|
40
36
|
from langtrace_python_sdk.instrumentation import (
|
|
41
37
|
AnthropicInstrumentation,
|
|
42
38
|
ChromaInstrumentation,
|
|
43
39
|
CohereInstrumentation,
|
|
44
40
|
CrewAIInstrumentation,
|
|
41
|
+
DspyInstrumentation,
|
|
45
42
|
EmbedchainInstrumentation,
|
|
43
|
+
GeminiInstrumentation,
|
|
46
44
|
GroqInstrumentation,
|
|
47
|
-
LangchainInstrumentation,
|
|
48
45
|
LangchainCommunityInstrumentation,
|
|
49
46
|
LangchainCoreInstrumentation,
|
|
47
|
+
LangchainInstrumentation,
|
|
50
48
|
LanggraphInstrumentation,
|
|
51
49
|
LlamaindexInstrumentation,
|
|
50
|
+
MistralInstrumentation,
|
|
51
|
+
OllamaInstrumentor,
|
|
52
52
|
OpenAIInstrumentation,
|
|
53
53
|
PineconeInstrumentation,
|
|
54
54
|
QdrantInstrumentation,
|
|
55
|
-
WeaviateInstrumentation,
|
|
56
|
-
OllamaInstrumentor,
|
|
57
|
-
DspyInstrumentation,
|
|
58
55
|
VertexAIInstrumentation,
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
WeaviateInstrumentation,
|
|
57
|
+
)
|
|
58
|
+
from langtrace_python_sdk.types import (
|
|
59
|
+
DisableInstrumentations,
|
|
60
|
+
InstrumentationMethods,
|
|
61
|
+
InstrumentationType,
|
|
61
62
|
)
|
|
62
|
-
from opentelemetry.instrumentation.sqlalchemy import SQLAlchemyInstrumentor
|
|
63
|
-
from colorama import Fore
|
|
64
63
|
from langtrace_python_sdk.utils import check_if_sdk_is_outdated
|
|
65
|
-
import
|
|
64
|
+
from langtrace_python_sdk.utils.langtrace_sampler import LangtraceSampler
|
|
66
65
|
|
|
67
66
|
|
|
68
67
|
def init(
|
|
@@ -80,11 +79,19 @@ def init(
|
|
|
80
79
|
sys.stdout = open(os.devnull, "w")
|
|
81
80
|
|
|
82
81
|
host = (
|
|
83
|
-
os.environ.get("LANGTRACE_API_HOST", None)
|
|
82
|
+
os.environ.get("LANGTRACE_API_HOST", None)
|
|
83
|
+
or os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None)
|
|
84
|
+
or os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", None)
|
|
85
|
+
or api_host
|
|
86
|
+
or LANGTRACE_REMOTE_URL
|
|
84
87
|
)
|
|
85
88
|
check_if_sdk_is_outdated()
|
|
86
89
|
print(Fore.GREEN + "Initializing Langtrace SDK.." + Fore.RESET)
|
|
87
|
-
print(
|
|
90
|
+
print(
|
|
91
|
+
Fore.WHITE
|
|
92
|
+
+ "⭐ Leave our github a star to stay on top of our updates - https://github.com/Scale3-Labs/langtrace"
|
|
93
|
+
+ Fore.RESET
|
|
94
|
+
)
|
|
88
95
|
sampler = LangtraceSampler(disabled_methods=disable_tracing_for_functions)
|
|
89
96
|
resource = Resource.create(
|
|
90
97
|
attributes={
|
langtrace_python_sdk/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2.3.
|
|
1
|
+
__version__ = "2.3.3"
|
|
@@ -11,6 +11,12 @@ examples/cohere_example/rerank.py,sha256=e7OU0A2FzfiQDuOmCy3Kg5LLNYGRmRIK5LqeLnT
|
|
|
11
11
|
examples/cohere_example/tools.py,sha256=a5uvS058tcwU6PJbF9EDO6LPVmPj2LoW4Vn8Web3Iq8,1656
|
|
12
12
|
examples/crewai_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
examples/crewai_example/basic.py,sha256=PBu4f8yQfZO1L_22UDm_ReU9lnEcycjZcGuy5UpgDJM,1948
|
|
14
|
+
examples/crewai_example/instagram_post/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
examples/crewai_example/instagram_post/agents.py,sha256=Acn5WnRUIzeN9k_VncCt7Egia18Tx5B7D3I983P45Ik,3986
|
|
16
|
+
examples/crewai_example/instagram_post/main.py,sha256=xVFAaOa8_LKS8VCRO8yXlCb_rYgRZAZvsq5pN2skkD4,2226
|
|
17
|
+
examples/crewai_example/instagram_post/tasks.py,sha256=aFNR2bnNxS66bz7sYXuw_Pxa2tVgqg42LlyDMsDYELM,6912
|
|
18
|
+
examples/crewai_example/instagram_post/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
examples/crewai_example/instagram_post/tools/browser_tools.py,sha256=XvnScN6EYkjITjCwJXMkP9xY8dNiDogi8Q9qG8ivlec,1889
|
|
14
20
|
examples/crewai_example/simple_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
21
|
examples/crewai_example/simple_agent/agents.py,sha256=IkjRlRZJgrT7fBtth9sSO7OEEs3IAe-tmZey4omedeI,2318
|
|
16
22
|
examples/crewai_example/simple_agent/main.py,sha256=YXWizVK80DTYFYZAIxLwLlVPKTRhaxoNxbZsdOJQAPw,1340
|
|
@@ -79,8 +85,8 @@ examples/vertexai_example/main.py,sha256=gndId5X5ksD-ycxnAWMdEqIDbLc3kz5Vt8vm4YP
|
|
|
79
85
|
examples/weaviate_example/__init__.py,sha256=8JMDBsRSEV10HfTd-YC7xb4txBjD3la56snk-Bbg2Kw,618
|
|
80
86
|
examples/weaviate_example/query_text.py,sha256=wPHQTc_58kPoKTZMygVjTj-2ZcdrIuaausJfMxNQnQc,127162
|
|
81
87
|
langtrace_python_sdk/__init__.py,sha256=VZM6i71NR7pBQK6XvJWRelknuTYUhqwqE7PlicKa5Wg,1166
|
|
82
|
-
langtrace_python_sdk/langtrace.py,sha256=
|
|
83
|
-
langtrace_python_sdk/version.py,sha256=
|
|
88
|
+
langtrace_python_sdk/langtrace.py,sha256=rjIEwPd-Iq0mcSCh_3CEK-38kyJwazdR1CfDa_U4mfQ,8468
|
|
89
|
+
langtrace_python_sdk/version.py,sha256=s0EEVOzZFl_WT6PzFxk9cgtfsNGRuqeXrX3fgGq9Ogs,22
|
|
84
90
|
langtrace_python_sdk/constants/__init__.py,sha256=P8QvYwt5czUNDZsKS64vxm9Dc41ptGbuF1TFtAF6nv4,44
|
|
85
91
|
langtrace_python_sdk/constants/exporter/langtrace_exporter.py,sha256=5MNjnAOg-4am78J3gVMH6FSwq5N8TOj72ugkhsw4vi0,46
|
|
86
92
|
langtrace_python_sdk/constants/instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -99,7 +105,7 @@ langtrace_python_sdk/constants/instrumentation/qdrant.py,sha256=yL7BopNQTXW7L7Z-
|
|
|
99
105
|
langtrace_python_sdk/constants/instrumentation/vertexai.py,sha256=0s2vX3Y0iwjOPkUg5lAKi-7o3LaNivDSBBbF-o695Ok,1266
|
|
100
106
|
langtrace_python_sdk/constants/instrumentation/weaviate.py,sha256=gtv-JBxvNGClEMxClmRKzjJ1khgOonsli4D_k9IagSE,2601
|
|
101
107
|
langtrace_python_sdk/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
102
|
-
langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=
|
|
108
|
+
langtrace_python_sdk/extensions/langtrace_exporter.py,sha256=nbDFxDsDRjpi7otiHHnQ3Z9_LYqLCgxdtP4IWnnOuyI,6307
|
|
103
109
|
langtrace_python_sdk/extensions/langtrace_filesystem.py,sha256=34fZutG28EJ66l67OvTGsydAH3ZpXgikdE7hVLqBpG4,7863
|
|
104
110
|
langtrace_python_sdk/instrumentation/__init__.py,sha256=bkyxh6lq_6dgCdbBseFQEbejRTLZv4s9nLBfNSqL6lk,1548
|
|
105
111
|
langtrace_python_sdk/instrumentation/anthropic/__init__.py,sha256=donrurJAGYlxrSRA3BIf76jGeUcAx9Tq8CVpah68S0Y,101
|
|
@@ -112,8 +118,8 @@ langtrace_python_sdk/instrumentation/cohere/__init__.py,sha256=sGUSLdTUyYf36Tm6L
|
|
|
112
118
|
langtrace_python_sdk/instrumentation/cohere/instrumentation.py,sha256=YQFHZIBd7SSPD4b6Va-ZR0thf_AuBCqj5yzHLHJVWnM,2121
|
|
113
119
|
langtrace_python_sdk/instrumentation/cohere/patch.py,sha256=Yb0OwxO4gG-WBfGhTFrwUUJEgpnRlyWI_FZveA4T1QU,20972
|
|
114
120
|
langtrace_python_sdk/instrumentation/crewai/__init__.py,sha256=_UBKfvQv7l0g2_wnmA5F6CdSAFH0atNOVPd49zsN3aM,88
|
|
115
|
-
langtrace_python_sdk/instrumentation/crewai/instrumentation.py,sha256=
|
|
116
|
-
langtrace_python_sdk/instrumentation/crewai/patch.py,sha256=
|
|
121
|
+
langtrace_python_sdk/instrumentation/crewai/instrumentation.py,sha256=5Umzq8zjEnMEtjZZiMB4DQOPkxZ-1vts7RKC6JWpn24,2969
|
|
122
|
+
langtrace_python_sdk/instrumentation/crewai/patch.py,sha256=oGpVnpq78q_O0t70XGHxYQoXv8Gy8L-s7HbM3ThPaB8,9313
|
|
117
123
|
langtrace_python_sdk/instrumentation/dspy/__init__.py,sha256=tM1srfi_QgyCzrde4izojMrRq2Wm7Dj5QUvVQXIJzkk,84
|
|
118
124
|
langtrace_python_sdk/instrumentation/dspy/instrumentation.py,sha256=o8URiDvCbZ8LL0I-4xKHkn_Ms2sETBRpn-gOliv3xzQ,2929
|
|
119
125
|
langtrace_python_sdk/instrumentation/dspy/patch.py,sha256=E2P3MJBQ71or4M6BsvZOwYFtJK1UdTsYkdxVj9fSWPs,9869
|
|
@@ -214,8 +220,8 @@ tests/pinecone/cassettes/test_query.yaml,sha256=b5v9G3ssUy00oG63PlFUR3JErF2Js-5A
|
|
|
214
220
|
tests/pinecone/cassettes/test_upsert.yaml,sha256=neWmQ1v3d03V8WoLl8FoFeeCYImb8pxlJBWnFd_lITU,38607
|
|
215
221
|
tests/qdrant/conftest.py,sha256=9n0uHxxIjWk9fbYc4bx-uP8lSAgLBVx-cV9UjnsyCHM,381
|
|
216
222
|
tests/qdrant/test_qdrant.py,sha256=pzjAjVY2kmsmGfrI2Gs2xrolfuaNHz7l1fqGQCjp5_o,3353
|
|
217
|
-
langtrace_python_sdk-2.3.
|
|
218
|
-
langtrace_python_sdk-2.3.
|
|
219
|
-
langtrace_python_sdk-2.3.
|
|
220
|
-
langtrace_python_sdk-2.3.
|
|
221
|
-
langtrace_python_sdk-2.3.
|
|
223
|
+
langtrace_python_sdk-2.3.3.dist-info/METADATA,sha256=f2TSpX8FY3eMW1DsIm21UOSmafBu4rbrvAnSYCJMfTY,15011
|
|
224
|
+
langtrace_python_sdk-2.3.3.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
225
|
+
langtrace_python_sdk-2.3.3.dist-info/entry_points.txt,sha256=1_b9-qvf2fE7uQNZcbUei9vLpFZBbbh9LrtGw95ssAo,70
|
|
226
|
+
langtrace_python_sdk-2.3.3.dist-info/licenses/LICENSE,sha256=QwcOLU5TJoTeUhuIXzhdCEEDDvorGiC6-3YTOl4TecE,11356
|
|
227
|
+
langtrace_python_sdk-2.3.3.dist-info/RECORD,,
|
|
File without changes
|
{langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{langtrace_python_sdk-2.3.1.dist-info → langtrace_python_sdk-2.3.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|