Skip to content

Commit a20edd9

Browse files
Release 2.1.21 (#211)
* remove logs * remove requirements * DSPy instrumentation support (#210) * DSPy instrumentation basic * Fix * Fix * remove hardcodings * Bump version
1 parent 5992327 commit a20edd9

File tree

12 files changed

+530
-2
lines changed

12 files changed

+530
-2
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import dspy
2+
from dspy.datasets.gsm8k import GSM8K, gsm8k_metric
3+
from dspy.teleprompt import BootstrapFewShot
4+
5+
# flake8: noqa
6+
from langtrace_python_sdk import langtrace, with_langtrace_root_span
7+
8+
langtrace.init()
9+
10+
turbo = dspy.OpenAI(model="gpt-3.5-turbo", max_tokens=250)
11+
dspy.settings.configure(lm=turbo)
12+
13+
# Load math questions from the GSM8K dataset
14+
gsm8k = GSM8K()
15+
gsm8k_trainset, gsm8k_devset = gsm8k.train[:10], gsm8k.dev[:10]
16+
17+
18+
class CoT(dspy.Module):
19+
def __init__(self):
20+
super().__init__()
21+
self.prog = dspy.ChainOfThought("question -> answer")
22+
23+
def forward(self, question):
24+
return self.prog(question=question)
25+
26+
27+
@with_langtrace_root_span(name="math_problems_cot_example")
28+
def example():
29+
30+
# Set up the optimizer: we want to "bootstrap" (i.e., self-generate) 4-shot examples of our CoT program.
31+
config = dict(max_bootstrapped_demos=4, max_labeled_demos=4)
32+
33+
# Optimize! Use the `gsm8k_metric` here. In general, the metric is going to tell the optimizer how well it's doing.
34+
teleprompter = BootstrapFewShot(metric=gsm8k_metric, **config)
35+
optimized_cot = teleprompter.compile(CoT(), trainset=gsm8k_trainset)
36+
37+
ans = optimized_cot(question="What is the sqrt of 345?")
38+
print(ans)
39+
40+
41+
if __name__ == "__main__":
42+
example()
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import dspy
2+
3+
# flake8: noqa
4+
from langtrace_python_sdk import langtrace, with_langtrace_root_span
5+
6+
langtrace.init()
7+
8+
turbo = dspy.OpenAI(model="gpt-3.5-turbo", max_tokens=250)
9+
dspy.settings.configure(lm=turbo)
10+
11+
12+
# Define a simple signature for basic question answering
13+
class BasicQA(dspy.Signature):
14+
"""Answer questions with short factoid answers."""
15+
16+
question = dspy.InputField()
17+
answer = dspy.OutputField(desc="often between 1 and 5 words")
18+
19+
20+
@with_langtrace_root_span(name="pot_example")
21+
def example():
22+
23+
# Pass signature to ProgramOfThought Module
24+
pot = dspy.ProgramOfThought(BasicQA)
25+
26+
# Call the ProgramOfThought module on a particular input
27+
question = "Sarah has 5 apples. She buys 7 more apples from the store. How many apples does Sarah have now?"
28+
result = pot(question=question)
29+
30+
print(f"Question: {question}")
31+
print(f"Final Predicted Answer (after ProgramOfThought process): {result.answer}")
32+
33+
34+
if __name__ == "__main__":
35+
example()
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
import dspy
2+
import json
3+
from dspy.datasets import HotPotQA
4+
from dspy.teleprompt import BootstrapFewShot
5+
from dspy.evaluate.evaluate import Evaluate
6+
7+
# flake8: noqa
8+
from langtrace_python_sdk import langtrace, with_langtrace_root_span
9+
10+
langtrace.init()
11+
12+
13+
colbertv2_wiki17_abstracts = dspy.ColBERTv2(
14+
url="http://20.102.90.50:2017/wiki17_abstracts"
15+
)
16+
dspy.settings.configure(rm=colbertv2_wiki17_abstracts)
17+
turbo = dspy.OpenAI(model="gpt-3.5-turbo-0613", max_tokens=500)
18+
dspy.settings.configure(lm=turbo, trace=[], temperature=0.7)
19+
20+
dataset = HotPotQA(
21+
train_seed=1,
22+
train_size=300,
23+
eval_seed=2023,
24+
dev_size=300,
25+
test_size=0,
26+
keep_details=True,
27+
)
28+
trainset = [x.with_inputs("question", "answer") for x in dataset.train]
29+
devset = [x.with_inputs("question", "answer") for x in dataset.dev]
30+
31+
32+
class GenerateAnswerChoices(dspy.Signature):
33+
"""Generate answer choices in JSON format that include the correct answer and plausible distractors for the specified question."""
34+
35+
question = dspy.InputField()
36+
correct_answer = dspy.InputField()
37+
number_of_choices = dspy.InputField()
38+
answer_choices = dspy.OutputField(desc="JSON key-value pairs")
39+
40+
41+
class QuizAnswerGenerator(dspy.Module):
42+
def __init__(self):
43+
super().__init__()
44+
self.prog = dspy.ChainOfThought(GenerateAnswerChoices)
45+
46+
def forward(self, question, answer):
47+
choices = self.prog(
48+
question=question, correct_answer=answer, number_of_choices="4"
49+
).answer_choices
50+
# dspy.Suggest(
51+
# format_checker(choices),
52+
# "The format of the answer choices should be in JSON format. Please revise accordingly.",
53+
# target_module=GenerateAnswerChoices,
54+
# )
55+
return dspy.Prediction(choices=choices)
56+
57+
58+
def format_checker(choice_string):
59+
try:
60+
choices = json.loads(choice_string)
61+
if isinstance(choices, dict) and all(
62+
isinstance(key, str) and isinstance(value, str)
63+
for key, value in choices.items()
64+
):
65+
return True
66+
except json.JSONDecodeError:
67+
return False
68+
69+
return False
70+
71+
72+
def format_valid_metric(gold, pred, trace=None):
73+
generated_choices = pred.choices
74+
format_valid = format_checker(generated_choices)
75+
score = format_valid
76+
return score
77+
78+
79+
@with_langtrace_root_span(name="quiz_generator_1")
80+
def quiz_generator_1():
81+
quiz_generator = QuizAnswerGenerator()
82+
83+
example = devset[67]
84+
print("Example Question: ", example.question)
85+
print("Example Answer: ", example.answer)
86+
# quiz_choices = quiz_generator(question=example.question, answer=example.answer)
87+
# print("Generated Quiz Choices: ", quiz_choices.choices)
88+
89+
optimizer = BootstrapFewShot(
90+
metric=format_valid_metric, max_bootstrapped_demos=4, max_labeled_demos=4
91+
)
92+
compiled_quiz_generator = optimizer.compile(
93+
quiz_generator,
94+
trainset=trainset,
95+
)
96+
quiz_choices = compiled_quiz_generator(
97+
question=example.question, answer=example.answer
98+
)
99+
print("Generated Quiz Choices: ", quiz_choices.choices)
100+
101+
# Evaluate
102+
evaluate = Evaluate(
103+
metric=format_valid_metric,
104+
devset=devset[67:70],
105+
num_threads=1,
106+
display_progress=True,
107+
display_table=5,
108+
)
109+
evaluate(quiz_generator)
110+
111+
112+
if __name__ == "__main__":
113+
quiz_generator_1()

src/examples/dspy_example/react.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import sys
2+
import os
3+
import dspy
4+
5+
# Add the local src folder to the Python path
6+
sys.path.insert(0, os.path.abspath('/Users/karthikkalyanaraman/work/langtrace/langtrace-python-sdk/src'))
7+
8+
# flake8: noqa
9+
from langtrace_python_sdk import langtrace, with_langtrace_root_span
10+
langtrace.init()
11+
12+
turbo = dspy.OpenAI(model='gpt-3.5-turbo', max_tokens=250)
13+
dspy.settings.configure(lm=turbo)
14+
15+
colbertv2_wiki17_abstracts = dspy.ColBERTv2(url='http://20.102.90.50:2017/wiki17_abstracts')
16+
dspy.settings.configure(rm=colbertv2_wiki17_abstracts)
17+
retriever = dspy.Retrieve(k=3)
18+
19+
# Define a simple signature for basic question answering
20+
class BasicQA(dspy.Signature):
21+
"""Answer questions with short factoid answers."""
22+
question = dspy.InputField()
23+
answer = dspy.OutputField(desc="often between 1 and 5 words")
24+
25+
@with_langtrace_root_span(name="react_example")
26+
def example():
27+
28+
# Pass signature to ReAct module
29+
react_module = dspy.ReAct(BasicQA)
30+
31+
# Call the ReAct module on a particular input
32+
question = 'Aside from the Apple Remote, what other devices can control the program Apple Remote was originally designed to interact with?'
33+
result = react_module(question=question)
34+
35+
print(f"Question: {question}")
36+
print(f"Final Predicted Answer (after ReAct process): {result.answer}")
37+
38+
if __name__ == '__main__':
39+
example()

src/langtrace_python_sdk/constants/instrumentation/common.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"ANTHROPIC": "Anthropic",
1212
"AZURE": "Azure",
1313
"CHROMA": "Chroma",
14+
"DSPY": "DSPy",
1415
"GROQ": "Groq",
1516
"LANGCHAIN": "Langchain",
1617
"LANGCHAIN_COMMUNITY": "Langchain Community",

src/langtrace_python_sdk/instrumentation/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .qdrant import QdrantInstrumentation
1313
from .weaviate import WeaviateInstrumentation
1414
from .ollama import OllamaInstrumentor
15+
from .dspy import DspyInstrumentor
1516

1617
__all__ = [
1718
"AnthropicInstrumentation",
@@ -28,4 +29,5 @@
2829
"QdrantInstrumentation",
2930
"WeaviateInstrumentation",
3031
"OllamaInstrumentor",
32+
"DspyInstrumentor",
3133
]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .instrumentation import DspyInstrumentor
2+
3+
__all__ = ["DspyInstrumentor"]
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
"""
2+
Copyright (c) 2024 Scale3 Labs
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
"""
16+
17+
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
18+
from opentelemetry.trace import get_tracer
19+
from wrapt import wrap_function_wrapper as _W
20+
from typing import Collection
21+
from importlib_metadata import version as v
22+
from .patch import patch_bootstrapfewshot_optimizer, patch_signature, patch_evaluate
23+
24+
25+
class DspyInstrumentor(BaseInstrumentor):
26+
"""
27+
The DspyInstrumentor class represents the DSPy instrumentation"""
28+
29+
def instrumentation_dependencies(self) -> Collection[str]:
30+
return ["dspy >= 0.1.5"]
31+
32+
def _instrument(self, **kwargs):
33+
tracer_provider = kwargs.get("tracer_provider")
34+
tracer = get_tracer(__name__, "", tracer_provider)
35+
version = v("dspy")
36+
_W(
37+
"dspy.teleprompt.bootstrap",
38+
"BootstrapFewShot.compile",
39+
patch_bootstrapfewshot_optimizer(
40+
"BootstrapFewShot.compile", version, tracer
41+
),
42+
)
43+
_W(
44+
"dspy.predict.predict",
45+
"Predict.forward",
46+
patch_signature("Predict.forward", version, tracer),
47+
)
48+
_W(
49+
"dspy.predict.chain_of_thought",
50+
"ChainOfThought.forward",
51+
patch_signature("ChainOfThought.forward", version, tracer),
52+
)
53+
_W(
54+
"dspy.predict.chain_of_thought_with_hint",
55+
"ChainOfThoughtWithHint.forward",
56+
patch_signature("ChainOfThoughtWithHint.forward", version, tracer),
57+
)
58+
_W(
59+
"dspy.predict.react",
60+
"ReAct.forward",
61+
patch_signature("ReAct.forward", version, tracer),
62+
)
63+
_W(
64+
"dspy.predict.program_of_thought",
65+
"ProgramOfThought.forward",
66+
patch_signature("ProgramOfThought.forward", version, tracer),
67+
)
68+
_W(
69+
"dspy.predict.multi_chain_comparison",
70+
"MultiChainComparison.forward",
71+
patch_signature("MultiChainComparison.forward", version, tracer),
72+
)
73+
_W(
74+
"dspy.predict.retry",
75+
"Retry.forward",
76+
patch_signature("Retry.forward", version, tracer),
77+
)
78+
_W(
79+
"dspy.evaluate.evaluate",
80+
"Evaluate.__call__",
81+
patch_evaluate("Evaluate", version, tracer),
82+
)
83+
84+
def _uninstrument(self, **kwargs):
85+
pass

0 commit comments

Comments
 (0)