langtrace-python-sdk 1.0.9__py3-none-any.whl → 1.0.11__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/__init__.py +0 -0
- examples/chroma_example/__init__.py +0 -0
- examples/chroma_example/basic.py +27 -0
- examples/langchain_example/__init__.py +0 -0
- examples/langchain_example/basic.py +65 -0
- examples/langchain_example/tool.py +85 -0
- examples/llamaindex_example/__init__.py +0 -0
- examples/llamaindex_example/basic.py +19 -0
- examples/openai/__init__.py +0 -0
- examples/openai/chat_completion.py +39 -0
- examples/openai/embeddings_create.py +19 -0
- examples/openai/function_calling.py +75 -0
- examples/openai/images_generate.py +20 -0
- examples/pinecone_example/__init__.py +0 -0
- examples/pinecone_example/basic.py +32 -0
- instrumentation/chroma/instrumentation.py +10 -4
- instrumentation/chroma/patch.py +8 -2
- instrumentation/constants.py +4 -9
- instrumentation/langchain_core/instrumentation.py +11 -5
- instrumentation/langchain_core/patch.py +24 -8
- instrumentation/llamaindex/instrumentation.py +6 -4
- instrumentation/llamaindex/patch.py +6 -1
- instrumentation/openai/{lib/apis.py → apis.py} +3 -0
- instrumentation/openai/{lib/constants.py → constants.py} +13 -0
- instrumentation/openai/patch.py +133 -74
- instrumentation/openai/token_estimation.py +48 -0
- instrumentation/pinecone/instrumentation.py +8 -2
- instrumentation/pinecone/patch.py +6 -4
- {langtrace_python_sdk-1.0.9.dist-info → langtrace_python_sdk-1.0.11.dist-info}/METADATA +1 -1
- langtrace_python_sdk-1.0.11.dist-info/RECORD +53 -0
- {langtrace_python_sdk-1.0.9.dist-info → langtrace_python_sdk-1.0.11.dist-info}/top_level.txt +1 -0
- instrumentation/utils.py +0 -27
- langtrace_python_sdk-1.0.9.dist-info/RECORD +0 -38
- {instrumentation → examples}/setup.py +0 -0
- /instrumentation/chroma/{lib/apis.py → apis.py} +0 -0
- /instrumentation/pinecone/{lib/apis.py → apis.py} +0 -0
- {langtrace_python_sdk-1.0.9.dist-info → langtrace_python_sdk-1.0.11.dist-info}/LICENSE +0 -0
- {langtrace_python_sdk-1.0.9.dist-info → langtrace_python_sdk-1.0.11.dist-info}/WHEEL +0 -0
examples/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import chromadb
|
|
2
|
+
from chromadb.utils import embedding_functions
|
|
3
|
+
from dotenv import find_dotenv, load_dotenv
|
|
4
|
+
|
|
5
|
+
from examples.setup import setup_instrumentation
|
|
6
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
7
|
+
|
|
8
|
+
_ = load_dotenv(find_dotenv())
|
|
9
|
+
|
|
10
|
+
setup_instrumentation()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@with_langtrace_root_span()
|
|
14
|
+
def basic():
|
|
15
|
+
chroma_client = chromadb.Client()
|
|
16
|
+
embedder = embedding_functions.DefaultEmbeddingFunction()
|
|
17
|
+
collection = chroma_client.create_collection(
|
|
18
|
+
name="my6_collection", embedding_function=embedder)
|
|
19
|
+
collection.add(
|
|
20
|
+
documents=["This is a document", "This is another document"],
|
|
21
|
+
metadatas=[{"source": "my_source"}, {"source": "my_source"}],
|
|
22
|
+
ids=["id1", "id2"]
|
|
23
|
+
)
|
|
24
|
+
results = collection.query(
|
|
25
|
+
query_texts=["This is a query document"],
|
|
26
|
+
n_results=2
|
|
27
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from langchain.text_splitter import RecursiveCharacterTextSplitter
|
|
3
|
+
from langchain_community.document_loaders import PyPDFLoader
|
|
4
|
+
from langchain_community.vectorstores.faiss import FAISS
|
|
5
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
6
|
+
from langchain_core.prompts.chat import ChatPromptTemplate
|
|
7
|
+
from langchain_core.runnables import RunnablePassthrough
|
|
8
|
+
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
|
|
9
|
+
|
|
10
|
+
from examples.setup import setup_instrumentation
|
|
11
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
12
|
+
|
|
13
|
+
_ = load_dotenv(find_dotenv())
|
|
14
|
+
|
|
15
|
+
setup_instrumentation()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@with_langtrace_root_span()
|
|
19
|
+
def basic():
|
|
20
|
+
llm = ChatOpenAI()
|
|
21
|
+
prompt = ChatPromptTemplate.from_messages([
|
|
22
|
+
("system", "You are world class technical documentation writer."),
|
|
23
|
+
("user", "{input}")
|
|
24
|
+
])
|
|
25
|
+
output_parser = StrOutputParser()
|
|
26
|
+
chain = prompt | llm | output_parser
|
|
27
|
+
res = chain.invoke({"input": "how can langsmith help with testing?"})
|
|
28
|
+
print(res)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@with_langtrace_root_span()
|
|
32
|
+
def rag():
|
|
33
|
+
vectorstore = FAISS.from_texts(
|
|
34
|
+
["harrison worked at kensho"], embedding=OpenAIEmbeddings()
|
|
35
|
+
)
|
|
36
|
+
retriever = vectorstore.as_retriever()
|
|
37
|
+
|
|
38
|
+
template = """Answer the question based only on the following context:{context}
|
|
39
|
+
|
|
40
|
+
Question: {question}
|
|
41
|
+
"""
|
|
42
|
+
prompt = ChatPromptTemplate.from_template(template)
|
|
43
|
+
|
|
44
|
+
model = ChatOpenAI()
|
|
45
|
+
|
|
46
|
+
chain = (
|
|
47
|
+
{"context": retriever, "question": RunnablePassthrough()}
|
|
48
|
+
| prompt
|
|
49
|
+
| model
|
|
50
|
+
| StrOutputParser()
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
res = chain.invoke("where did harrison work?")
|
|
54
|
+
# print(res)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@with_langtrace_root_span()
|
|
58
|
+
def load_and_split():
|
|
59
|
+
url = 'https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf'
|
|
60
|
+
loader = PyPDFLoader(url)
|
|
61
|
+
data = loader.load()
|
|
62
|
+
text_splitter = RecursiveCharacterTextSplitter(
|
|
63
|
+
chunk_size=500, chunk_overlap=0)
|
|
64
|
+
docs = text_splitter.split_documents(data)
|
|
65
|
+
# print(docs)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from langchain import hub
|
|
3
|
+
from langchain.agents import AgentExecutor, create_openai_functions_agent
|
|
4
|
+
from langchain.chains import LLMMathChain
|
|
5
|
+
from langchain_core.pydantic_v1 import BaseModel, Field
|
|
6
|
+
from langchain_core.tools import Tool
|
|
7
|
+
from langchain_openai import ChatOpenAI
|
|
8
|
+
|
|
9
|
+
from examples.setup import setup_instrumentation
|
|
10
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
11
|
+
|
|
12
|
+
_ = load_dotenv(find_dotenv())
|
|
13
|
+
|
|
14
|
+
setup_instrumentation()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
llm = ChatOpenAI(temperature=0, model="gpt-4")
|
|
18
|
+
llm_math_chain = LLMMathChain.from_llm(llm=llm, verbose=True)
|
|
19
|
+
|
|
20
|
+
primes = {998: 7901, 999: 7907, 1000: 7919}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CalculatorInput(BaseModel):
|
|
24
|
+
question: str = Field()
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class PrimeInput(BaseModel):
|
|
28
|
+
n: int = Field()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def is_prime(n: int) -> bool:
|
|
32
|
+
if n <= 1 or (n % 2 == 0 and n > 2):
|
|
33
|
+
return False
|
|
34
|
+
for i in range(3, int(n**0.5) + 1, 2):
|
|
35
|
+
if n % i == 0:
|
|
36
|
+
return False
|
|
37
|
+
return True
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_prime(n: int, primes: dict = primes) -> str:
|
|
41
|
+
return str(primes.get(int(n)))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def aget_prime(n: int, primes: dict = primes) -> str:
|
|
45
|
+
return str(primes.get(int(n)))
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@with_langtrace_root_span()
|
|
49
|
+
def tool_example():
|
|
50
|
+
|
|
51
|
+
tools = [
|
|
52
|
+
Tool(
|
|
53
|
+
name="GetPrime",
|
|
54
|
+
func=get_prime,
|
|
55
|
+
description="A tool that returns the `n`th prime number",
|
|
56
|
+
args_schema=PrimeInput,
|
|
57
|
+
coroutine=aget_prime,
|
|
58
|
+
),
|
|
59
|
+
Tool.from_function(
|
|
60
|
+
func=llm_math_chain.run,
|
|
61
|
+
name="Calculator",
|
|
62
|
+
description="Useful for when you need to compute mathematical expressions",
|
|
63
|
+
args_schema=CalculatorInput,
|
|
64
|
+
coroutine=llm_math_chain.arun,
|
|
65
|
+
),
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
prompt = hub.pull("hwchase17/openai-functions-agent")
|
|
69
|
+
|
|
70
|
+
agent = create_openai_functions_agent(llm, tools, prompt)
|
|
71
|
+
|
|
72
|
+
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
|
73
|
+
|
|
74
|
+
question = "What is the product of the 998th, 999th and 1000th prime numbers?"
|
|
75
|
+
|
|
76
|
+
for step in agent_executor.iter({"input": question}):
|
|
77
|
+
if output := step.get("intermediate_step"):
|
|
78
|
+
action, value = output[0]
|
|
79
|
+
if action.tool == "GetPrime":
|
|
80
|
+
print(f"Checking whether {value} is prime...")
|
|
81
|
+
assert is_prime(int(value))
|
|
82
|
+
# Ask user if they want to continue
|
|
83
|
+
_continue = input("Should the agent continue (Y/n)?:\n") or "Y"
|
|
84
|
+
if _continue.lower() != "y":
|
|
85
|
+
break
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from llama_index.core import SimpleDirectoryReader, VectorStoreIndex
|
|
3
|
+
|
|
4
|
+
from examples.setup import setup_instrumentation
|
|
5
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
6
|
+
|
|
7
|
+
_ = load_dotenv(find_dotenv())
|
|
8
|
+
|
|
9
|
+
setup_instrumentation()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@with_langtrace_root_span()
|
|
13
|
+
def basic():
|
|
14
|
+
documents = SimpleDirectoryReader(
|
|
15
|
+
"src/examples/llamaindex_example/data").load_data()
|
|
16
|
+
index = VectorStoreIndex.from_documents(documents)
|
|
17
|
+
query_engine = index.as_query_engine()
|
|
18
|
+
response = query_engine.query("What did the author do in college?")
|
|
19
|
+
print(response)
|
|
File without changes
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from openai import OpenAI
|
|
3
|
+
|
|
4
|
+
from examples.setup import setup_instrumentation
|
|
5
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
6
|
+
|
|
7
|
+
_ = load_dotenv(find_dotenv())
|
|
8
|
+
|
|
9
|
+
setup_instrumentation()
|
|
10
|
+
|
|
11
|
+
client = OpenAI()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@with_langtrace_root_span()
|
|
15
|
+
def chat_completion():
|
|
16
|
+
response = client.chat.completions.create(
|
|
17
|
+
model="gpt-4",
|
|
18
|
+
messages=[{"role": "user", "content": "Say this is a test three times"}],
|
|
19
|
+
stream=True,
|
|
20
|
+
)
|
|
21
|
+
# print(stream)
|
|
22
|
+
# stream = client.chat.completions.create(
|
|
23
|
+
# model="gpt-4",
|
|
24
|
+
# messages=[{"role": "user", "content": "Say this is a test three times"}, {"role": "assistant", "content": "This is a test. This is a test. This is a test"},
|
|
25
|
+
# {"role": "user", "content": "Say this is a mock 4 times"}],
|
|
26
|
+
# stream=False,
|
|
27
|
+
# )
|
|
28
|
+
|
|
29
|
+
result = []
|
|
30
|
+
for chunk in response:
|
|
31
|
+
if chunk.choices[0].delta.function_call is not None:
|
|
32
|
+
content = [
|
|
33
|
+
choice.delta.function_call.arguments if choice.delta.function_call and
|
|
34
|
+
choice.delta.function_call.arguments else ""
|
|
35
|
+
for choice in chunk.choices]
|
|
36
|
+
result.append(
|
|
37
|
+
content[0] if len(content) > 0 else "")
|
|
38
|
+
|
|
39
|
+
print("".join(result))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from openai import OpenAI
|
|
3
|
+
|
|
4
|
+
from examples.setup import setup_instrumentation
|
|
5
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
6
|
+
|
|
7
|
+
_ = load_dotenv(find_dotenv())
|
|
8
|
+
|
|
9
|
+
setup_instrumentation()
|
|
10
|
+
|
|
11
|
+
client = OpenAI()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@with_langtrace_root_span()
|
|
15
|
+
def embeddings_create():
|
|
16
|
+
result = client.embeddings.create(
|
|
17
|
+
model="text-embedding-ada-002",
|
|
18
|
+
input="Once upon a time, there was a frog.",
|
|
19
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
3
|
+
from dotenv import find_dotenv, load_dotenv
|
|
4
|
+
from openai import OpenAI
|
|
5
|
+
|
|
6
|
+
from examples.setup import setup_instrumentation
|
|
7
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
8
|
+
|
|
9
|
+
_ = load_dotenv(find_dotenv())
|
|
10
|
+
|
|
11
|
+
setup_instrumentation()
|
|
12
|
+
|
|
13
|
+
client = OpenAI()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
student_custom_functions = [
|
|
17
|
+
{
|
|
18
|
+
'name': 'extract_student_info',
|
|
19
|
+
'description': 'Get the student information from the body of the input text',
|
|
20
|
+
'parameters': {
|
|
21
|
+
'type': 'object',
|
|
22
|
+
'properties': {
|
|
23
|
+
'name': {
|
|
24
|
+
'type': 'string',
|
|
25
|
+
'description': 'Name of the person'
|
|
26
|
+
},
|
|
27
|
+
'major': {
|
|
28
|
+
'type': 'string',
|
|
29
|
+
'description': 'Major subject.'
|
|
30
|
+
},
|
|
31
|
+
'school': {
|
|
32
|
+
'type': 'string',
|
|
33
|
+
'description': 'The university name.'
|
|
34
|
+
},
|
|
35
|
+
'grades': {
|
|
36
|
+
'type': 'integer',
|
|
37
|
+
'description': 'GPA of the student.'
|
|
38
|
+
},
|
|
39
|
+
'club': {
|
|
40
|
+
'type': 'string',
|
|
41
|
+
'description': 'School club for extracurricular activities. '
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@with_langtrace_root_span()
|
|
51
|
+
def function_calling():
|
|
52
|
+
response = client.chat.completions.create(
|
|
53
|
+
model='gpt-3.5-turbo',
|
|
54
|
+
messages=[{'role': 'user', 'content': "David Nguyen is a sophomore majoring in computer science at Stanford University. He is Asian American and has a 3.8 GPA. David is known for his programming skills and is an active member of the university's Robotics Club. He hopes to pursue a career in artificial intelligence after graduating."}],
|
|
55
|
+
functions=student_custom_functions,
|
|
56
|
+
function_call='auto',
|
|
57
|
+
stream=False
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# result = []
|
|
61
|
+
# for chunk in response:
|
|
62
|
+
# if chunk.choices[0].delta.function_call is not None:
|
|
63
|
+
# content = [
|
|
64
|
+
# choice.delta.function_call.arguments if choice.delta.function_call and
|
|
65
|
+
# choice.delta.function_call.arguments else ""
|
|
66
|
+
# for choice in chunk.choices]
|
|
67
|
+
# result.append(
|
|
68
|
+
# content[0] if len(content) > 0 else "")
|
|
69
|
+
|
|
70
|
+
# print("".join(result))
|
|
71
|
+
|
|
72
|
+
# Loading the response as a JSON object
|
|
73
|
+
json_response = json.loads(
|
|
74
|
+
response.choices[0].message.function_call.arguments)
|
|
75
|
+
print(json_response)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from openai import OpenAI
|
|
3
|
+
|
|
4
|
+
from examples.setup import setup_instrumentation
|
|
5
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
6
|
+
|
|
7
|
+
_ = load_dotenv(find_dotenv())
|
|
8
|
+
|
|
9
|
+
setup_instrumentation()
|
|
10
|
+
|
|
11
|
+
client = OpenAI()
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@with_langtrace_root_span()
|
|
15
|
+
def images_generate():
|
|
16
|
+
result = client.images.generate(
|
|
17
|
+
model="dall-e-3",
|
|
18
|
+
prompt="A cute baby sea otter",
|
|
19
|
+
)
|
|
20
|
+
print(result)
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from dotenv import find_dotenv, load_dotenv
|
|
2
|
+
from openai import OpenAI
|
|
3
|
+
from pinecone import Pinecone
|
|
4
|
+
|
|
5
|
+
from examples.setup import setup_instrumentation
|
|
6
|
+
from instrumentation.with_root_span import with_langtrace_root_span
|
|
7
|
+
|
|
8
|
+
_ = load_dotenv(find_dotenv())
|
|
9
|
+
|
|
10
|
+
setup_instrumentation()
|
|
11
|
+
|
|
12
|
+
client = OpenAI()
|
|
13
|
+
pinecone = Pinecone()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@with_langtrace_root_span()
|
|
17
|
+
def basic():
|
|
18
|
+
result = client.embeddings.create(
|
|
19
|
+
model="text-embedding-ada-002",
|
|
20
|
+
input="Some random text string goes here",
|
|
21
|
+
encoding_format="float"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
embedding = result.data[0].embedding
|
|
25
|
+
|
|
26
|
+
unique_id = "randomid"
|
|
27
|
+
data_to_upsert = {"id": unique_id, "values": embedding}
|
|
28
|
+
|
|
29
|
+
index = pinecone.Index("test-index")
|
|
30
|
+
index.upsert(vectors=[data_to_upsert])
|
|
31
|
+
|
|
32
|
+
resp = index.query(vector=embedding, top_k=1)
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Instrumentation for ChromaDB
|
|
3
|
+
"""
|
|
1
4
|
import importlib.metadata
|
|
2
5
|
from typing import Collection
|
|
3
6
|
|
|
@@ -5,11 +8,14 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
|
5
8
|
from opentelemetry.trace import get_tracer
|
|
6
9
|
from wrapt import wrap_function_wrapper
|
|
7
10
|
|
|
8
|
-
from instrumentation.chroma.
|
|
11
|
+
from instrumentation.chroma.apis import APIS
|
|
9
12
|
from instrumentation.chroma.patch import collection_patch
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
class ChromaInstrumentation(BaseInstrumentor):
|
|
16
|
+
"""
|
|
17
|
+
The ChromaInstrumentation class represents the ChromaDB instrumentation
|
|
18
|
+
"""
|
|
13
19
|
|
|
14
20
|
def instrumentation_dependencies(self) -> Collection[str]:
|
|
15
21
|
return ["chromadb >= 0.4.23"]
|
|
@@ -19,7 +25,7 @@ class ChromaInstrumentation(BaseInstrumentor):
|
|
|
19
25
|
tracer = get_tracer(__name__, "", tracer_provider)
|
|
20
26
|
version = importlib.metadata.version('chromadb')
|
|
21
27
|
|
|
22
|
-
for operation,
|
|
28
|
+
for operation, _ in APIS.items():
|
|
23
29
|
wrap_function_wrapper(
|
|
24
30
|
'chromadb.api.models.Collection',
|
|
25
31
|
f'Collection.{operation.lower()}',
|
|
@@ -27,7 +33,7 @@ class ChromaInstrumentation(BaseInstrumentor):
|
|
|
27
33
|
)
|
|
28
34
|
|
|
29
35
|
def _instrument_module(self, module_name):
|
|
30
|
-
|
|
36
|
+
pass
|
|
31
37
|
|
|
32
38
|
def _uninstrument(self, **kwargs):
|
|
33
|
-
|
|
39
|
+
pass
|
instrumentation/chroma/patch.py
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the patching logic for the Chroma client.
|
|
3
|
+
"""
|
|
1
4
|
from langtrace.trace_attributes import DatabaseSpanAttributes
|
|
2
|
-
from opentelemetry.trace import SpanKind
|
|
5
|
+
from opentelemetry.trace import SpanKind
|
|
3
6
|
from opentelemetry.trace.status import Status, StatusCode
|
|
4
7
|
|
|
8
|
+
from instrumentation.chroma.apis import APIS
|
|
5
9
|
from instrumentation.constants import SERVICE_PROVIDERS
|
|
6
|
-
from instrumentation.chroma.lib.apis import APIS
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
def collection_patch(method, version, tracer):
|
|
13
|
+
"""
|
|
14
|
+
A generic patch method that wraps a function with a span
|
|
15
|
+
"""
|
|
10
16
|
def traced_method(wrapped, instance, args, kwargs):
|
|
11
17
|
api = APIS[method]
|
|
12
18
|
service_provider = SERVICE_PROVIDERS['CHROMA']
|
instrumentation/constants.py
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
"LANGCHAIN": "Langtrace Langchain SDK",
|
|
5
|
-
"PINECONE": "Langtrace Pinecone SDK",
|
|
6
|
-
"LLAMAINDEX": "Langtrace LlamaIndex SDK",
|
|
7
|
-
"CHROMA": "Langtrace Chroma SDK",
|
|
8
|
-
}
|
|
9
|
-
|
|
1
|
+
"""
|
|
2
|
+
This file contains the constants used in the project.
|
|
3
|
+
"""
|
|
10
4
|
SERVICE_PROVIDERS = {
|
|
11
5
|
"OPENAI": "OpenAI",
|
|
12
6
|
"AZURE": "Azure",
|
|
13
7
|
"LANGCHAIN": "Langchain",
|
|
8
|
+
"LANGCHAIN_CORE": "Langchain Core",
|
|
14
9
|
"LANGCHAIN_COMMUNITY": "Langchain Community",
|
|
15
10
|
"PINECONE": "Pinecone",
|
|
16
11
|
"LLAMAINDEX": "LlamaIndex",
|
|
@@ -71,15 +71,21 @@ class LangchainCoreInstrumentation(BaseInstrumentor):
|
|
|
71
71
|
version = importlib.metadata.version('langchain-core')
|
|
72
72
|
|
|
73
73
|
exclude_methods = ['get_name', 'get_output_schema',
|
|
74
|
-
'get_input_schema', 'get_graph', 'to_json'
|
|
75
|
-
|
|
74
|
+
'get_input_schema', 'get_graph', 'to_json',
|
|
75
|
+
'to_json_not_implemented', 'bind', 'dict',
|
|
76
|
+
'format', 'format_messages', 'format_prompt']
|
|
77
|
+
exclude_classes = ['BaseChatPromptTemplate', 'Runnable', 'RunnableBinding',
|
|
78
|
+
'RunnableBindingBase', 'RunnableEach', 'RunnableEachBase',
|
|
79
|
+
'RunnableGenerator', 'RunnablePick', 'RunnableMap',
|
|
80
|
+
'RunnableSerializable']
|
|
81
|
+
|
|
76
82
|
modules_to_patch = [
|
|
77
83
|
('langchain_core.retrievers', 'retriever',
|
|
78
84
|
generic_patch, True, True),
|
|
79
|
-
('langchain_core.prompts.chat', '
|
|
80
|
-
generic_patch, True,
|
|
85
|
+
('langchain_core.prompts.chat', 'prompt',
|
|
86
|
+
generic_patch, True, True),
|
|
81
87
|
('langchain_core.runnables.base',
|
|
82
|
-
'
|
|
88
|
+
'runnable', runnable_patch, True, True),
|
|
83
89
|
('langchain_core.runnables.passthrough',
|
|
84
90
|
'runnablepassthrough', runnable_patch, True, True),
|
|
85
91
|
('langchain_core.output_parsers.string',
|
|
@@ -22,7 +22,7 @@ def generic_patch(method_name, task, tracer, version, trace_output=True, trace_i
|
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
24
|
def traced_method(wrapped, instance, args, kwargs):
|
|
25
|
-
service_provider = SERVICE_PROVIDERS['
|
|
25
|
+
service_provider = SERVICE_PROVIDERS['LANGCHAIN_CORE']
|
|
26
26
|
span_attributes = {
|
|
27
27
|
'langtrace.service.name': service_provider,
|
|
28
28
|
'langtrace.service.type': 'framework',
|
|
@@ -32,7 +32,18 @@ def generic_patch(method_name, task, tracer, version, trace_output=True, trace_i
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
if len(args) > 0 and trace_input:
|
|
35
|
-
|
|
35
|
+
inputs = {}
|
|
36
|
+
for arg in args:
|
|
37
|
+
if isinstance(arg, dict):
|
|
38
|
+
for key, value in arg.items():
|
|
39
|
+
if isinstance(value, list):
|
|
40
|
+
for item in value:
|
|
41
|
+
inputs[key] = item.__class__.__name__
|
|
42
|
+
elif isinstance(value, str):
|
|
43
|
+
inputs[key] = value
|
|
44
|
+
elif isinstance(arg, str):
|
|
45
|
+
inputs['input'] = arg
|
|
46
|
+
span_attributes['langchain.inputs'] = to_json_string(inputs)
|
|
36
47
|
|
|
37
48
|
attributes = FrameworkSpanAttributes(**span_attributes)
|
|
38
49
|
|
|
@@ -73,7 +84,7 @@ def runnable_patch(method_name, task, tracer, version, trace_output=True, trace_
|
|
|
73
84
|
trace_input: Whether to trace the input of the patched methods.
|
|
74
85
|
"""
|
|
75
86
|
def traced_method(wrapped, instance, args, kwargs):
|
|
76
|
-
service_provider = SERVICE_PROVIDERS['
|
|
87
|
+
service_provider = SERVICE_PROVIDERS['LANGCHAIN_CORE']
|
|
77
88
|
span_attributes = {
|
|
78
89
|
'langtrace.service.name': service_provider,
|
|
79
90
|
'langtrace.service.type': 'framework',
|
|
@@ -84,12 +95,17 @@ def runnable_patch(method_name, task, tracer, version, trace_output=True, trace_
|
|
|
84
95
|
|
|
85
96
|
if trace_input:
|
|
86
97
|
inputs = {}
|
|
87
|
-
args_list = []
|
|
88
98
|
if len(args) > 0:
|
|
89
|
-
for
|
|
90
|
-
if isinstance(
|
|
91
|
-
|
|
92
|
-
|
|
99
|
+
for arg in args:
|
|
100
|
+
if isinstance(arg, dict):
|
|
101
|
+
for key, value in arg.items():
|
|
102
|
+
if isinstance(value, list):
|
|
103
|
+
for item in value:
|
|
104
|
+
inputs[key] = item.__class__.__name__
|
|
105
|
+
elif isinstance(value, str):
|
|
106
|
+
inputs[key] = value
|
|
107
|
+
elif isinstance(arg, str):
|
|
108
|
+
inputs['input'] = arg
|
|
93
109
|
|
|
94
110
|
for field, value in instance.steps.items() if hasattr(instance, "steps") and \
|
|
95
111
|
isinstance(instance.steps, dict) else {}:
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The LlamaindexInstrumentation class represents the LlamaIndex instrumentation
|
|
3
|
+
"""
|
|
1
4
|
import importlib.metadata
|
|
2
5
|
from typing import Collection
|
|
3
6
|
|
|
@@ -8,12 +11,11 @@ from wrapt import wrap_function_wrapper
|
|
|
8
11
|
|
|
9
12
|
from instrumentation.llamaindex.patch import generic_patch
|
|
10
13
|
|
|
11
|
-
MODULES = [
|
|
12
|
-
"llama_index.core.query_pipeline.query",
|
|
13
|
-
]
|
|
14
|
-
|
|
15
14
|
|
|
16
15
|
class LlamaindexInstrumentation(BaseInstrumentor):
|
|
16
|
+
"""
|
|
17
|
+
The LlamaindexInstrumentation class represents the LlamaIndex instrumentation
|
|
18
|
+
"""
|
|
17
19
|
|
|
18
20
|
def instrumentation_dependencies(self) -> Collection[str]:
|
|
19
21
|
return ["llama-index >= 0.10.0"]
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains a generic patch method that wraps a function with a span.
|
|
3
|
+
"""
|
|
1
4
|
from langtrace.trace_attributes import FrameworkSpanAttributes
|
|
2
|
-
from opentelemetry.trace import SpanKind
|
|
5
|
+
from opentelemetry.trace import SpanKind
|
|
3
6
|
from opentelemetry.trace.status import Status, StatusCode
|
|
4
7
|
|
|
5
8
|
from instrumentation.constants import SERVICE_PROVIDERS
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
def generic_patch(method, task, tracer, version):
|
|
12
|
+
"""
|
|
13
|
+
A generic patch method that wraps a function with a span"""
|
|
9
14
|
def traced_method(wrapped, instance, args, kwargs):
|
|
10
15
|
service_provider = SERVICE_PROVIDERS['LLAMAINDEX']
|
|
11
16
|
span_attributes = {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Constants for OpenAI API"""
|
|
3
|
+
|
|
1
4
|
OPENAI_COST_TABLE = {
|
|
2
5
|
"gpt-4-0125-preview": {
|
|
3
6
|
"input": 0.01,
|
|
@@ -28,3 +31,13 @@ OPENAI_COST_TABLE = {
|
|
|
28
31
|
"output": 0.002,
|
|
29
32
|
},
|
|
30
33
|
}
|
|
34
|
+
|
|
35
|
+
# TODO: Add more models
|
|
36
|
+
# https://github.com/dqbd/tiktoken/blob/74c147e19584a3a1acea0c8e0da4d39415cd33e0/wasm/src/lib.rs#L328
|
|
37
|
+
TIKTOKEN_MODEL_MAPPING = {
|
|
38
|
+
"gpt-4": "cl100k_base",
|
|
39
|
+
"gpt-4-32k": "cl100k_base",
|
|
40
|
+
"gpt-4-0125-preview": "cl100k_base",
|
|
41
|
+
"gpt-4-1106-preview": "cl100k_base",
|
|
42
|
+
"gpt-4-1106-vision-preview": "cl100k_base",
|
|
43
|
+
}
|
instrumentation/openai/patch.py
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the patching logic for the OpenAI library."""
|
|
1
3
|
import json
|
|
2
4
|
|
|
3
5
|
from langtrace.trace_attributes import Event, LLMSpanAttributes
|
|
4
|
-
from opentelemetry.trace import SpanKind
|
|
6
|
+
from opentelemetry.trace import SpanKind
|
|
5
7
|
from opentelemetry.trace.status import Status, StatusCode
|
|
6
8
|
|
|
7
9
|
from instrumentation.constants import SERVICE_PROVIDERS
|
|
8
|
-
from instrumentation.openai.
|
|
10
|
+
from instrumentation.openai.apis import APIS
|
|
11
|
+
from instrumentation.openai.token_estimation import (calculate_prompt_tokens,
|
|
12
|
+
estimate_tokens)
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
def images_generate(original_method, version, tracer):
|
|
16
|
+
"""
|
|
17
|
+
Wrap the `generate` method of the `Images` class to trace it.
|
|
18
|
+
"""
|
|
12
19
|
def traced_method(wrapped, instance, args, kwargs):
|
|
13
20
|
base_url = str(instance._client._base_url) if hasattr(
|
|
14
21
|
instance, '_client') and hasattr(instance._client, '_base_url') else ""
|
|
@@ -27,7 +34,8 @@ def images_generate(original_method, version, tracer):
|
|
|
27
34
|
|
|
28
35
|
attributes = LLMSpanAttributes(**span_attributes)
|
|
29
36
|
|
|
30
|
-
with tracer.start_as_current_span(APIS["IMAGES_GENERATION"]["METHOD"],
|
|
37
|
+
with tracer.start_as_current_span(APIS["IMAGES_GENERATION"]["METHOD"],
|
|
38
|
+
kind=SpanKind.CLIENT) as span:
|
|
31
39
|
for field, value in attributes.model_dump(by_alias=True).items():
|
|
32
40
|
if value is not None:
|
|
33
41
|
span.set_attribute(field, value)
|
|
@@ -39,7 +47,8 @@ def images_generate(original_method, version, tracer):
|
|
|
39
47
|
result, 'data') and len(result.data) > 0 else {}
|
|
40
48
|
response = [{
|
|
41
49
|
"url": data.url if hasattr(data, 'url') else "",
|
|
42
|
-
"revised_prompt": data.revised_prompt if
|
|
50
|
+
"revised_prompt": data.revised_prompt if
|
|
51
|
+
hasattr(data, 'revised_prompt') else "",
|
|
43
52
|
}]
|
|
44
53
|
span.set_attribute(
|
|
45
54
|
"llm.responses", json.dumps(response))
|
|
@@ -60,6 +69,7 @@ def images_generate(original_method, version, tracer):
|
|
|
60
69
|
|
|
61
70
|
|
|
62
71
|
def chat_completions_create(original_method, version, tracer):
|
|
72
|
+
"""Wrap the `create` method of the `ChatCompletion` class to trace it."""
|
|
63
73
|
def traced_method(wrapped, instance, args, kwargs):
|
|
64
74
|
base_url = str(instance._client._base_url) if hasattr(
|
|
65
75
|
instance, '_client') and hasattr(instance._client, '_base_url') else ""
|
|
@@ -84,83 +94,131 @@ def chat_completions_create(original_method, version, tracer):
|
|
|
84
94
|
attributes.llm_top_p = kwargs.get('top_p')
|
|
85
95
|
if kwargs.get('user') is not None:
|
|
86
96
|
attributes.llm_user = kwargs.get('user')
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
97
|
+
if kwargs.get('functions') is not None:
|
|
98
|
+
attributes.llm_function_prompts = json.dumps(
|
|
99
|
+
kwargs.get('functions'))
|
|
100
|
+
|
|
101
|
+
# TODO(Karthik): Gotta figure out how to handle streaming with context
|
|
102
|
+
# with tracer.start_as_current_span(APIS["CHAT_COMPLETION"]["METHOD"],
|
|
103
|
+
# kind=SpanKind.CLIENT) as span:
|
|
104
|
+
span = tracer.start_span(
|
|
105
|
+
APIS["CHAT_COMPLETION"]["METHOD"], kind=SpanKind.CLIENT)
|
|
106
|
+
for field, value in attributes.model_dump(by_alias=True).items():
|
|
107
|
+
if value is not None:
|
|
108
|
+
span.set_attribute(field, value)
|
|
109
|
+
try:
|
|
110
|
+
# Attempt to call the original method
|
|
111
|
+
result = original_method(*args, **kwargs)
|
|
112
|
+
if kwargs.get('stream') is False:
|
|
113
|
+
if hasattr(result, 'choices') and result.choices is not None:
|
|
114
|
+
responses = [
|
|
115
|
+
{
|
|
116
|
+
"message": choice.message.content if choice.message and
|
|
117
|
+
choice.message.content else choice.message.function_call.arguments
|
|
118
|
+
if choice.message and
|
|
119
|
+
choice.message.function_call.arguments else "",
|
|
120
|
+
**({"content_filter_results": choice["content_filter_results"]}
|
|
121
|
+
if "content_filter_results" in choice else {})
|
|
122
|
+
}
|
|
123
|
+
for choice in result.choices
|
|
124
|
+
]
|
|
125
|
+
span.set_attribute(
|
|
126
|
+
"llm.responses", json.dumps(responses))
|
|
127
|
+
else:
|
|
128
|
+
responses = []
|
|
129
|
+
span.set_attribute(
|
|
130
|
+
"llm.responses", json.dumps(responses))
|
|
131
|
+
if hasattr(result, 'system_fingerprint') and \
|
|
132
|
+
result.system_fingerprint is not None:
|
|
133
|
+
span.set_attribute(
|
|
134
|
+
"llm.system.fingerprint", result.system_fingerprint)
|
|
135
|
+
# Get the usage
|
|
136
|
+
if hasattr(result, 'usage') and result.usage is not None:
|
|
137
|
+
usage = result.usage
|
|
138
|
+
if usage is not None:
|
|
139
|
+
usage_dict = {
|
|
140
|
+
"prompt_tokens": result.usage.prompt_tokens,
|
|
141
|
+
"completion_tokens": usage.completion_tokens,
|
|
142
|
+
"total_tokens": usage.total_tokens
|
|
143
|
+
}
|
|
109
144
|
span.set_attribute(
|
|
110
|
-
"llm.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
145
|
+
"llm.token.counts", json.dumps(usage_dict))
|
|
146
|
+
span.set_status(StatusCode.OK)
|
|
147
|
+
span.end()
|
|
148
|
+
return result
|
|
149
|
+
else:
|
|
150
|
+
prompt_tokens = calculate_prompt_tokens(json.dumps(
|
|
151
|
+
kwargs.get('messages', {})[0]), kwargs.get('model'))
|
|
152
|
+
return handle_streaming_response(result, span, prompt_tokens,
|
|
153
|
+
function_call=kwargs.get(
|
|
154
|
+
'functions')
|
|
155
|
+
is not None)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
# Record the exception in the span
|
|
158
|
+
span.record_exception(e)
|
|
159
|
+
# Set the span status to indicate an error
|
|
160
|
+
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
161
|
+
# Reraise the exception to ensure it's not swallowed
|
|
162
|
+
span.end()
|
|
163
|
+
raise
|
|
164
|
+
|
|
165
|
+
def handle_streaming_response(result, span, prompt_tokens, function_call=False):
|
|
166
|
+
"""Process and yield streaming response chunks."""
|
|
167
|
+
result_content = []
|
|
168
|
+
span.add_event(Event.STREAM_START.value)
|
|
169
|
+
completion_tokens = 0
|
|
170
|
+
try:
|
|
171
|
+
for chunk in result:
|
|
172
|
+
if hasattr(chunk, 'choices') and chunk.choices is not None:
|
|
173
|
+
token_counts = [
|
|
174
|
+
estimate_tokens(choice.delta.content) if choice.delta
|
|
175
|
+
and choice.delta.content
|
|
176
|
+
else estimate_tokens(choice.delta.function_call.arguments)
|
|
177
|
+
if choice.delta.function_call and
|
|
178
|
+
choice.delta.function_call.arguments else 0
|
|
179
|
+
for choice in chunk.choices
|
|
180
|
+
]
|
|
181
|
+
completion_tokens += sum(token_counts)
|
|
182
|
+
content = [
|
|
183
|
+
choice.delta.content if choice.delta and choice.delta.content
|
|
184
|
+
else choice.delta.function_call.arguments if choice.delta.function_call and
|
|
185
|
+
choice.delta.function_call.arguments else ""
|
|
186
|
+
for choice in chunk.choices
|
|
187
|
+
]
|
|
126
188
|
else:
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
span.record_exception(e)
|
|
152
|
-
|
|
153
|
-
# Set the span status to indicate an error
|
|
154
|
-
span.set_status(Status(StatusCode.ERROR, str(e)))
|
|
155
|
-
|
|
156
|
-
# Reraise the exception to ensure it's not swallowed
|
|
157
|
-
raise
|
|
189
|
+
content = []
|
|
190
|
+
span.add_event(Event.STREAM_OUTPUT.value, {
|
|
191
|
+
"response": "".join(content)
|
|
192
|
+
})
|
|
193
|
+
result_content.append(
|
|
194
|
+
content[0] if len(content) > 0 else "")
|
|
195
|
+
yield chunk
|
|
196
|
+
finally:
|
|
197
|
+
|
|
198
|
+
# Finalize span after processing all chunks
|
|
199
|
+
span.add_event(Event.STREAM_END.value)
|
|
200
|
+
span.set_attribute("llm.token.counts", json.dumps({
|
|
201
|
+
"prompt_tokens": prompt_tokens,
|
|
202
|
+
"completion_tokens": completion_tokens,
|
|
203
|
+
"total_tokens": prompt_tokens + completion_tokens
|
|
204
|
+
}))
|
|
205
|
+
if function_call is False:
|
|
206
|
+
span.set_attribute("llm.responses", json.dumps(
|
|
207
|
+
{"message": {"role": "assistant", "content": "".join(result_content)}}))
|
|
208
|
+
else:
|
|
209
|
+
span.set_attribute("llm.responses", json.dumps(
|
|
210
|
+
{"message": {"role": "assistant", "function_call": "".join(result_content)}}))
|
|
211
|
+
span.set_status(StatusCode.OK)
|
|
212
|
+
span.end()
|
|
158
213
|
|
|
159
214
|
# return the wrapped method
|
|
160
215
|
return traced_method
|
|
161
216
|
|
|
162
217
|
|
|
163
218
|
def embeddings_create(original_method, version, tracer):
|
|
219
|
+
"""
|
|
220
|
+
Wrap the `create` method of the `Embeddings` class to trace it.
|
|
221
|
+
"""
|
|
164
222
|
def traced_method(wrapped, instance, args, kwargs):
|
|
165
223
|
base_url = str(instance._client._base_url) if hasattr(
|
|
166
224
|
instance, '_client') and hasattr(instance._client, '_base_url') else ""
|
|
@@ -187,7 +245,8 @@ def embeddings_create(original_method, version, tracer):
|
|
|
187
245
|
if kwargs.get('user') is not None:
|
|
188
246
|
attributes["llm.user"] = kwargs.get('user')
|
|
189
247
|
|
|
190
|
-
with tracer.start_as_current_span(APIS["EMBEDDINGS_CREATE"]["METHOD"],
|
|
248
|
+
with tracer.start_as_current_span(APIS["EMBEDDINGS_CREATE"]["METHOD"],
|
|
249
|
+
kind=SpanKind.CLIENT) as span:
|
|
191
250
|
for field, value in attributes.model_dump(by_alias=True).items():
|
|
192
251
|
if value is not None:
|
|
193
252
|
span.set_attribute(field, value)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains functions to estimate the number of tokens in a prompt and
|
|
3
|
+
to calculate the price of a model based on its usage.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from tiktoken import get_encoding
|
|
7
|
+
|
|
8
|
+
from instrumentation.openai.constants import (OPENAI_COST_TABLE,
|
|
9
|
+
TIKTOKEN_MODEL_MAPPING)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def estimate_tokens(prompt):
|
|
13
|
+
"""
|
|
14
|
+
Estimate the number of tokens in a prompt."""
|
|
15
|
+
if prompt and len(prompt) > 0:
|
|
16
|
+
# Simplified token estimation: count the words.
|
|
17
|
+
return len([word for word in prompt.split() if word])
|
|
18
|
+
return 0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def estimate_tokens_using_tiktoken(prompt, model):
|
|
22
|
+
"""
|
|
23
|
+
Estimate the number of tokens in a prompt using tiktoken."""
|
|
24
|
+
encoding = get_encoding(model)
|
|
25
|
+
tokens = encoding.encode(prompt)
|
|
26
|
+
return len(tokens)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def calculate_prompt_tokens(prompt_content, model):
|
|
30
|
+
"""
|
|
31
|
+
Calculate the number of tokens in a prompt. If the model is supported by tiktoken, use it for the estimation."""
|
|
32
|
+
try:
|
|
33
|
+
tiktoken_model = TIKTOKEN_MODEL_MAPPING[model]
|
|
34
|
+
return estimate_tokens_using_tiktoken(prompt_content, tiktoken_model)
|
|
35
|
+
except Exception:
|
|
36
|
+
return estimate_tokens(prompt_content) # Fallback method
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def calculate_price_from_usage(model, usage):
|
|
40
|
+
"""
|
|
41
|
+
Calculate the price of a model based on its usage."""
|
|
42
|
+
cost_table = OPENAI_COST_TABLE.get(model)
|
|
43
|
+
if cost_table:
|
|
44
|
+
return (
|
|
45
|
+
(cost_table['input'] * usage['prompt_tokens'] +
|
|
46
|
+
cost_table['output'] * usage['completion_tokens']) / 1000
|
|
47
|
+
)
|
|
48
|
+
return 0
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pinecone instrumentation
|
|
3
|
+
"""
|
|
4
|
+
|
|
1
5
|
import importlib.metadata
|
|
2
6
|
from typing import Collection
|
|
3
7
|
|
|
@@ -7,11 +11,13 @@ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
|
|
|
7
11
|
from opentelemetry.trace import get_tracer
|
|
8
12
|
from wrapt import wrap_function_wrapper
|
|
9
13
|
|
|
10
|
-
from instrumentation.pinecone.
|
|
14
|
+
from instrumentation.pinecone.apis import APIS
|
|
11
15
|
from instrumentation.pinecone.patch import generic_patch
|
|
12
16
|
|
|
13
17
|
|
|
14
18
|
class PineconeInstrumentation(BaseInstrumentor):
|
|
19
|
+
"""
|
|
20
|
+
The PineconeInstrumentation class represents the Pinecone instrumentation"""
|
|
15
21
|
|
|
16
22
|
def instrumentation_dependencies(self) -> Collection[str]:
|
|
17
23
|
return ["pinecone-client >= 3.1.0"]
|
|
@@ -40,4 +46,4 @@ class PineconeInstrumentation(BaseInstrumentor):
|
|
|
40
46
|
)
|
|
41
47
|
|
|
42
48
|
def _uninstrument(self, **kwargs):
|
|
43
|
-
|
|
49
|
+
pass
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"""
|
|
2
|
+
This module contains the patching logic for the Pinecone client."""
|
|
3
3
|
from langtrace.trace_attributes import DatabaseSpanAttributes
|
|
4
|
-
from opentelemetry.trace import SpanKind
|
|
4
|
+
from opentelemetry.trace import SpanKind
|
|
5
5
|
from opentelemetry.trace.status import Status, StatusCode
|
|
6
6
|
|
|
7
7
|
from instrumentation.constants import SERVICE_PROVIDERS
|
|
8
|
-
from instrumentation.pinecone.
|
|
8
|
+
from instrumentation.pinecone.apis import APIS
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def generic_patch(original_method, method, version, tracer):
|
|
12
|
+
"""
|
|
13
|
+
A generic patch method that wraps a function with a span"""
|
|
12
14
|
def traced_method(wrapped, instance, args, kwargs):
|
|
13
15
|
api = APIS[method]
|
|
14
16
|
service_provider = SERVICE_PROVIDERS['PINECONE']
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
examples/setup.py,sha256=07Sy36lUuNuPU5QPAs2BEMm-YKSosruzKJPl0QKc_rc,2105
|
|
3
|
+
examples/chroma_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
examples/chroma_example/basic.py,sha256=HD8vGSsvcXN21AuWyUx09CePphXrIp9PSNllueMqvEI,836
|
|
5
|
+
examples/langchain_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
examples/langchain_example/basic.py,sha256=PBYotFDJlkH9y17U8WRbH3FxqgfBPHra2k_C7hHPiMc,1993
|
|
7
|
+
examples/langchain_example/tool.py,sha256=g7mFuXal6TtCvo-KTYqWeSEpuyQMR2LuHF6WQdKRwbw,2494
|
|
8
|
+
examples/llamaindex_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
examples/llamaindex_example/basic.py,sha256=b1y1VnYE7eaeSXLk9rJ4EZApCj-tMPPMTY-fc_M7X7c,620
|
|
10
|
+
examples/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
+
examples/openai/chat_completion.py,sha256=3ojkccgntJp_IXnNb7f7w9sXPtj_mDNOE7IyLB6SsS0,1300
|
|
12
|
+
examples/openai/embeddings_create.py,sha256=01ApMJ2F_8-IWanDAD8SWlrcjg8b3mJzaddsGhRvrck,457
|
|
13
|
+
examples/openai/function_calling.py,sha256=MzxNNvPNdUWOw8Ugs_gMGuwyQN4yPJnja46rE_8W_ZY,2475
|
|
14
|
+
examples/openai/images_generate.py,sha256=GD2Bj902xqS0pfZr0pZtPXCGHEHWZM_CjGJ0DdoP_pY,444
|
|
15
|
+
examples/pinecone_example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
examples/pinecone_example/basic.py,sha256=lF8TcDvW1v6j-QKvl3B9r1t0ShnvCcOgJ5F74qy6VO8,793
|
|
17
|
+
instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
+
instrumentation/constants.py,sha256=YdC62dsYpbbBdMHhfUbaK-cbDM4w4eau0ClUdWVmjmU,336
|
|
19
|
+
instrumentation/with_root_span.py,sha256=CRie2ljHhnHN8bUGDwBM-F18-c6xyoI_238KP8BEO-U,969
|
|
20
|
+
instrumentation/chroma/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
+
instrumentation/chroma/apis.py,sha256=hiPGYdHS0Yj4Kh3eaYBbuCAl_swqIygu80yFqkOgdak,955
|
|
22
|
+
instrumentation/chroma/instrumentation.py,sha256=ySEyLnXcjL7D3sgMHTkxwdpxDpsRVbRJvFOgTxYRHvs,1174
|
|
23
|
+
instrumentation/chroma/patch.py,sha256=2ERORLV4_F1UU2Is8Y4H7aEXNnAPI5XKvW9DnLeEndM,1943
|
|
24
|
+
instrumentation/chroma/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
|
+
instrumentation/langchain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
+
instrumentation/langchain/instrumentation.py,sha256=LXtx5edfHPLRZ9yP0yKbDHlvE7LOJumJMGTqQX5RhhM,2850
|
|
27
|
+
instrumentation/langchain/patch.py,sha256=f-lq0wdk7doop-Dak2VcGueDsESA_5RKyuGtJQIm4DQ,2979
|
|
28
|
+
instrumentation/langchain_community/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
29
|
+
instrumentation/langchain_community/instrumentation.py,sha256=XWzaHl4FPPlZEhnUlxRB0iO5kPkNVI9siRXZgxF_Yb4,4316
|
|
30
|
+
instrumentation/langchain_community/patch.py,sha256=w6R_lHTDg2GzWRH8BZNocQowedeaNUE-pLfCoRETnTk,2872
|
|
31
|
+
instrumentation/langchain_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
|
+
instrumentation/langchain_core/instrumentation.py,sha256=AYHqyuOwunuMqrTCxzXCbkIDmJbHA6Q40moHntdL4tw,4739
|
|
33
|
+
instrumentation/langchain_core/patch.py,sha256=a314C0IaF0gSa2eh-yO8jHqtZwnWIczQsF3FgCgoiiU,7536
|
|
34
|
+
instrumentation/llamaindex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
+
instrumentation/llamaindex/instrumentation.py,sha256=WwETor8jLwaDQwnwgbtKZHQ3MwtNIfZSp1aaUn-uLIk,2759
|
|
36
|
+
instrumentation/llamaindex/patch.py,sha256=hSSoOij70kIhAleHLOfTW-zNc-N9boQz1iyhoBdVRsQ,1709
|
|
37
|
+
instrumentation/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
instrumentation/openai/apis.py,sha256=lMUa6rCkT-fKltngOUxcXd0aNpTb5L8xlqjrdseLIZM,488
|
|
39
|
+
instrumentation/openai/constants.py,sha256=3_xaFfAhh2dpH0072Cijzb5iZazUpmj4SF0iWiMFm1A,973
|
|
40
|
+
instrumentation/openai/instrumentation.py,sha256=Mkk6fwvQ8kS9ykFFc8OffIGbNMYVi6rrBVjVVhjuTjo,1408
|
|
41
|
+
instrumentation/openai/patch.py,sha256=Syktbjz9R-XjGj9QEGojZ4fsxZdQ7Gq4nPOW49J4fLA,12234
|
|
42
|
+
instrumentation/openai/token_estimation.py,sha256=nwTR0yyZs2OB0S3bBviBgd_xMb6oh7nCPMt-HlIlCRU,1549
|
|
43
|
+
instrumentation/openai/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
44
|
+
instrumentation/pinecone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
45
|
+
instrumentation/pinecone/apis.py,sha256=XpKNUfyzEE3HkBN10Qv1w_t1PT-J39pHlotrdU-wvec,477
|
|
46
|
+
instrumentation/pinecone/instrumentation.py,sha256=yfOxKkMtW6GEUQ0E9AWSBdaa07MHzV3o6Q09cAvoWIU,1708
|
|
47
|
+
instrumentation/pinecone/patch.py,sha256=fr07o97CqGc8sUEyMtSiT6watZiTPStRPOrxOzhJGLo,1840
|
|
48
|
+
instrumentation/pinecone/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
|
+
langtrace_python_sdk-1.0.11.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
50
|
+
langtrace_python_sdk-1.0.11.dist-info/METADATA,sha256=Zgg1c6Ykg62bBICe3rPmogg4GWjTe-WwtnGWQyVJ_mA,6149
|
|
51
|
+
langtrace_python_sdk-1.0.11.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
52
|
+
langtrace_python_sdk-1.0.11.dist-info/top_level.txt,sha256=FJhLokAk3p9qB-lzJKTfQ1CtAXoxvyL8zkfffVrFSdI,25
|
|
53
|
+
langtrace_python_sdk-1.0.11.dist-info/RECORD,,
|
instrumentation/utils.py
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from tiktoken import TiktokenEncoding, get_encoding
|
|
2
|
-
from .constants import TIKTOKEN_MODEL_MAPPING, OPENAI_COST_TABLE
|
|
3
|
-
|
|
4
|
-
def estimate_tokens(prompt: str) -> int:
|
|
5
|
-
if prompt and len(prompt) > 0:
|
|
6
|
-
# Simplified token estimation: count the words.
|
|
7
|
-
return len(prompt.split())
|
|
8
|
-
return 0
|
|
9
|
-
|
|
10
|
-
def estimate_tokens_using_tiktoken(prompt: str, model: TiktokenEncoding) -> int:
|
|
11
|
-
encoding = get_encoding(model)
|
|
12
|
-
tokens = encoding.encode(prompt)
|
|
13
|
-
return len(tokens)
|
|
14
|
-
|
|
15
|
-
def calculate_prompt_tokens(prompt_content: str, model: str) -> int:
|
|
16
|
-
try:
|
|
17
|
-
tiktoken_model = TIKTOKEN_MODEL_MAPPING[model]
|
|
18
|
-
return estimate_tokens_using_tiktoken(prompt_content, tiktoken_model)
|
|
19
|
-
except KeyError:
|
|
20
|
-
return estimate_tokens(prompt_content) # Fallback method
|
|
21
|
-
|
|
22
|
-
def calculate_price_from_usage(model: str, usage: dict) -> float:
|
|
23
|
-
cost_table = OPENAI_COST_TABLE.get(model)
|
|
24
|
-
if cost_table:
|
|
25
|
-
return ((cost_table['input'] * usage['prompt_tokens'] +
|
|
26
|
-
cost_table['output'] * usage['completion_tokens']) / 1000)
|
|
27
|
-
return 0
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
instrumentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
instrumentation/constants.py,sha256=1wth9_em4_h4UgmcADcPmDHvKCJLqOqBbTw7jmcga6A,467
|
|
3
|
-
instrumentation/setup.py,sha256=07Sy36lUuNuPU5QPAs2BEMm-YKSosruzKJPl0QKc_rc,2105
|
|
4
|
-
instrumentation/utils.py,sha256=2kQHQgeuk8kSGoQSBQByQYEXGLkSMDkw7riYVag_cv8,1059
|
|
5
|
-
instrumentation/with_root_span.py,sha256=CRie2ljHhnHN8bUGDwBM-F18-c6xyoI_238KP8BEO-U,969
|
|
6
|
-
instrumentation/chroma/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
instrumentation/chroma/instrumentation.py,sha256=Gc0nNAMoBR-WPRbgPw5AYZBDSQUY56i_rAEbhX8h4A4,1078
|
|
8
|
-
instrumentation/chroma/patch.py,sha256=smzSItC53-tuaMuGbiMhgB2luNJOHz3ttyL7uOqyYII,1811
|
|
9
|
-
instrumentation/chroma/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
instrumentation/chroma/lib/apis.py,sha256=hiPGYdHS0Yj4Kh3eaYBbuCAl_swqIygu80yFqkOgdak,955
|
|
11
|
-
instrumentation/langchain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
-
instrumentation/langchain/instrumentation.py,sha256=LXtx5edfHPLRZ9yP0yKbDHlvE7LOJumJMGTqQX5RhhM,2850
|
|
13
|
-
instrumentation/langchain/patch.py,sha256=f-lq0wdk7doop-Dak2VcGueDsESA_5RKyuGtJQIm4DQ,2979
|
|
14
|
-
instrumentation/langchain_community/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
instrumentation/langchain_community/instrumentation.py,sha256=XWzaHl4FPPlZEhnUlxRB0iO5kPkNVI9siRXZgxF_Yb4,4316
|
|
16
|
-
instrumentation/langchain_community/patch.py,sha256=w6R_lHTDg2GzWRH8BZNocQowedeaNUE-pLfCoRETnTk,2872
|
|
17
|
-
instrumentation/langchain_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
instrumentation/langchain_core/instrumentation.py,sha256=uq9F0R0AVM-mlqcrtl2cKxFjHjMTqTZb8741AVhzjAA,4360
|
|
19
|
-
instrumentation/langchain_core/patch.py,sha256=PRAi64V02TSY3uX3403dmQ0CDPHSf9ZF9fvw4Tfaf3Y,6702
|
|
20
|
-
instrumentation/llamaindex/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
instrumentation/llamaindex/instrumentation.py,sha256=igUTU0lXOVGXOF_4fvFrRa0Y8dmwdGpk6AqWmrQhDaE,2635
|
|
22
|
-
instrumentation/llamaindex/patch.py,sha256=-vsnJBXBBUwNJckHfmH8BRuVM0_dHGOqH6d22NBY4_E,1562
|
|
23
|
-
instrumentation/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
24
|
-
instrumentation/openai/instrumentation.py,sha256=Mkk6fwvQ8kS9ykFFc8OffIGbNMYVi6rrBVjVVhjuTjo,1408
|
|
25
|
-
instrumentation/openai/patch.py,sha256=hA71WogWtbP3gIGAE9cVMF3UyBOQPV-KyLrg50ctfoM,9467
|
|
26
|
-
instrumentation/openai/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
27
|
-
instrumentation/openai/lib/apis.py,sha256=9rVa-9nKkKWuIFKGmyrFK_fHe7XVe07SlfRfB3Bxl8Q,453
|
|
28
|
-
instrumentation/openai/lib/constants.py,sha256=jbxBAXlyauJQFQhM5I01005y0qLr_IRRWjKDkzsltDA,594
|
|
29
|
-
instrumentation/pinecone/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
instrumentation/pinecone/instrumentation.py,sha256=asVu7d7slGOwv0ZTv9W_oIOe9gSDI7dFgxLhh-JMUt0,1598
|
|
31
|
-
instrumentation/pinecone/patch.py,sha256=KCiNToy2hAq0eQHyR-Jw5aEyyn1XoQubGrzew-_sK04,1725
|
|
32
|
-
instrumentation/pinecone/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
|
-
instrumentation/pinecone/lib/apis.py,sha256=XpKNUfyzEE3HkBN10Qv1w_t1PT-J39pHlotrdU-wvec,477
|
|
34
|
-
langtrace_python_sdk-1.0.9.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
35
|
-
langtrace_python_sdk-1.0.9.dist-info/METADATA,sha256=ZDDY0OWZ-TMVH3SF8198BOOuTAZfdaN7qLQD9iYNAbU,6148
|
|
36
|
-
langtrace_python_sdk-1.0.9.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
|
37
|
-
langtrace_python_sdk-1.0.9.dist-info/top_level.txt,sha256=mdFAULSZuqUiDveRElCIPMvwAkRAYXP4bm_dEI4A96Q,16
|
|
38
|
-
langtrace_python_sdk-1.0.9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|