langwatch 0.2.7__py3-none-any.whl → 0.2.9__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.
- langwatch/__version__.py +1 -1
- langwatch/dspy/__init__.py +80 -9
- langwatch/telemetry/tracing.py +3 -2
- {langwatch-0.2.7.dist-info → langwatch-0.2.9.dist-info}/METADATA +1 -1
- {langwatch-0.2.7.dist-info → langwatch-0.2.9.dist-info}/RECORD +6 -6
- {langwatch-0.2.7.dist-info → langwatch-0.2.9.dist-info}/WHEEL +0 -0
langwatch/__version__.py
CHANGED
langwatch/dspy/__init__.py
CHANGED
|
@@ -9,7 +9,6 @@ from langwatch.utils.transformation import truncate_object_recursively
|
|
|
9
9
|
from langwatch.telemetry.tracing import LangWatchTrace
|
|
10
10
|
from typing_extensions import TypedDict
|
|
11
11
|
import langwatch
|
|
12
|
-
from langwatch.state import get_api_key, get_endpoint
|
|
13
12
|
import httpx
|
|
14
13
|
import json
|
|
15
14
|
from pydantic import BaseModel
|
|
@@ -21,6 +20,7 @@ from dspy.teleprompt import (
|
|
|
21
20
|
COPRO,
|
|
22
21
|
MIPROv2,
|
|
23
22
|
)
|
|
23
|
+
import dspy.teleprompt.copro_optimizer as copro_optimizer
|
|
24
24
|
from dspy.signatures.signature import SignatureMeta
|
|
25
25
|
from dspy.primitives.prediction import Prediction, Completions
|
|
26
26
|
from dspy.primitives.example import Example
|
|
@@ -28,6 +28,7 @@ from pydantic.fields import FieldInfo
|
|
|
28
28
|
from coolname import generate_slug
|
|
29
29
|
from retry import retry
|
|
30
30
|
from dspy.evaluate.evaluate import Evaluate
|
|
31
|
+
from dspy.utils.callback import with_callbacks
|
|
31
32
|
|
|
32
33
|
|
|
33
34
|
class SerializableAndPydanticEncoder(json.JSONEncoder):
|
|
@@ -72,7 +73,16 @@ class SerializableAndPydanticEncoder(json.JSONEncoder):
|
|
|
72
73
|
if isinstance(o, Completions):
|
|
73
74
|
return {"__class__": classname} | o.__dict__
|
|
74
75
|
if isinstance(o, BaseModel):
|
|
75
|
-
|
|
76
|
+
try:
|
|
77
|
+
return o.model_dump(exclude_unset=True)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
if 'MockValSer' in str(e):
|
|
80
|
+
return {
|
|
81
|
+
key: getattr(o, key)
|
|
82
|
+
for key in o.model_fields.keys()
|
|
83
|
+
if hasattr(o, key) and getattr(o, key) is not None
|
|
84
|
+
}
|
|
85
|
+
raise
|
|
76
86
|
try:
|
|
77
87
|
return super().default(o)
|
|
78
88
|
except:
|
|
@@ -152,11 +162,12 @@ class LangWatchDSPy:
|
|
|
152
162
|
def init(
|
|
153
163
|
self,
|
|
154
164
|
experiment: str,
|
|
155
|
-
optimizer: Optional[Teleprompter],
|
|
165
|
+
optimizer: Optional[Teleprompter] = None,
|
|
156
166
|
run_id: Optional[str] = None,
|
|
157
167
|
slug: Optional[str] = None,
|
|
158
168
|
workflow_id: Optional[str] = None,
|
|
159
169
|
workflow_version_id: Optional[str] = None,
|
|
170
|
+
evaluator: Optional[Evaluate] = None,
|
|
160
171
|
):
|
|
161
172
|
if langwatch.get_api_key() is None:
|
|
162
173
|
print("API key was not detected, calling langwatch.login()...")
|
|
@@ -184,6 +195,9 @@ class LangWatchDSPy:
|
|
|
184
195
|
)
|
|
185
196
|
response.raise_for_status()
|
|
186
197
|
|
|
198
|
+
if optimizer and evaluator:
|
|
199
|
+
raise ValueError("You can only provide an optimizer or an evaluator, not both.")
|
|
200
|
+
|
|
187
201
|
self.experiment_slug = slug or experiment
|
|
188
202
|
random.seed() # MIPRO meses up the global seed, so we need to reset it to random to get a new run_id
|
|
189
203
|
self.run_id = run_id or generate_slug(3)
|
|
@@ -193,9 +207,11 @@ class LangWatchDSPy:
|
|
|
193
207
|
self.patch_llms()
|
|
194
208
|
if optimizer is not None:
|
|
195
209
|
self.patch_optimizer(optimizer)
|
|
210
|
+
elif evaluator is not None:
|
|
211
|
+
self.patch_evaluator(evaluator)
|
|
196
212
|
else:
|
|
197
213
|
print(
|
|
198
|
-
"No optimizer provided, assuming custom optimizer tracking, make sure to call `track_metric` and `log_step` manually: https://docs.langwatch.ai/dspy-visualization/custom-optimizer"
|
|
214
|
+
"No optimizer or evaluator provided, assuming custom optimizer tracking, make sure to call `track_metric` and `log_step` manually: https://docs.langwatch.ai/dspy-visualization/custom-optimizer"
|
|
199
215
|
)
|
|
200
216
|
|
|
201
217
|
result = response.json()
|
|
@@ -205,6 +221,10 @@ class LangWatchDSPy:
|
|
|
205
221
|
f"[LangWatch] Open {langwatch.get_endpoint()}{self.experiment_path}?runIds={self.run_id} to track your DSPy training session live\n"
|
|
206
222
|
)
|
|
207
223
|
|
|
224
|
+
def patch_evaluator(self, evaluator: Evaluate):
|
|
225
|
+
evaluator.__class__ = LangWatchTrackedEvaluate
|
|
226
|
+
print(f"\n[LangWatch] `dspy.evaluate.Evaluate` object detected and patched for live tracking.")
|
|
227
|
+
|
|
208
228
|
def patch_optimizer(self, optimizer: Teleprompter):
|
|
209
229
|
METRIC_TRACKING_CLASSMAP = {
|
|
210
230
|
BootstrapFewShot: LangWatchTrackedBootstrapFewShot,
|
|
@@ -379,14 +399,16 @@ langwatch_dspy = LangWatchDSPy()
|
|
|
379
399
|
|
|
380
400
|
def init(
|
|
381
401
|
experiment: str,
|
|
382
|
-
optimizer: Optional[Teleprompter],
|
|
402
|
+
optimizer: Optional[Teleprompter] = None,
|
|
383
403
|
run_id: Optional[str] = None,
|
|
384
404
|
slug: Optional[str] = None,
|
|
385
405
|
workflow_id: Optional[str] = None,
|
|
386
406
|
workflow_version_id: Optional[str] = None,
|
|
407
|
+
evaluator: Optional[Evaluate] = None,
|
|
387
408
|
):
|
|
388
409
|
langwatch_dspy.init(
|
|
389
|
-
experiment, optimizer, run_id, slug, workflow_id, workflow_version_id
|
|
410
|
+
experiment, optimizer, run_id, slug, workflow_id, workflow_version_id,
|
|
411
|
+
evaluator=evaluator,
|
|
390
412
|
)
|
|
391
413
|
|
|
392
414
|
|
|
@@ -521,7 +543,15 @@ class LangWatchTrackedCOPRO(COPRO):
|
|
|
521
543
|
|
|
522
544
|
@contextmanager
|
|
523
545
|
def _patch_logger_and_evaluate(self):
|
|
524
|
-
|
|
546
|
+
legacy_logger = True
|
|
547
|
+
original_logger_info = None
|
|
548
|
+
|
|
549
|
+
if hasattr(copro_optimizer, "logger"):
|
|
550
|
+
legacy_logger = False
|
|
551
|
+
original_logger_info = copro_optimizer.logger.info
|
|
552
|
+
else:
|
|
553
|
+
original_logger_info = dspy.logger.info
|
|
554
|
+
|
|
525
555
|
original_evaluate_call = Evaluate.__call__
|
|
526
556
|
step = None
|
|
527
557
|
scores = []
|
|
@@ -577,13 +607,20 @@ class LangWatchTrackedCOPRO(COPRO):
|
|
|
577
607
|
|
|
578
608
|
return score
|
|
579
609
|
|
|
580
|
-
|
|
610
|
+
if legacy_logger:
|
|
611
|
+
dspy.logger.info = patched_logger_info
|
|
612
|
+
else:
|
|
613
|
+
copro_optimizer.logger.info = patched_logger_info
|
|
614
|
+
|
|
581
615
|
Evaluate.__call__ = patched_evaluate_call
|
|
582
616
|
|
|
583
617
|
try:
|
|
584
618
|
yield
|
|
585
619
|
finally:
|
|
586
|
-
|
|
620
|
+
if legacy_logger:
|
|
621
|
+
dspy.logger.info = original_logger_info
|
|
622
|
+
else:
|
|
623
|
+
copro_optimizer.logger.info = original_logger_info
|
|
587
624
|
Evaluate.__call__ = original_evaluate_call
|
|
588
625
|
|
|
589
626
|
|
|
@@ -658,6 +695,40 @@ class LangWatchTrackedMIPROv2(MIPROv2):
|
|
|
658
695
|
Evaluate.__call__ = original_evaluate_call
|
|
659
696
|
|
|
660
697
|
|
|
698
|
+
class LangWatchTrackedEvaluate(Evaluate):
|
|
699
|
+
@with_callbacks
|
|
700
|
+
def __call__(self, program: dspy.Module, metric: Optional[Callable] = None, **kwargs):
|
|
701
|
+
lw_dspy = langwatch_dspy
|
|
702
|
+
|
|
703
|
+
metric_to_use = metric if metric is not None else self.metric
|
|
704
|
+
if not metric_to_use:
|
|
705
|
+
raise ValueError("Evaluation metric must be provided either during Evaluate initialization or during call.")
|
|
706
|
+
|
|
707
|
+
wrapped_metric = lw_dspy.track_metric(metric_to_use)
|
|
708
|
+
|
|
709
|
+
# Call the original evaluation logic with the wrapped metric.
|
|
710
|
+
# We need to make sure we pass the wrapped metric to the super call.
|
|
711
|
+
kwargs_for_super = kwargs.copy()
|
|
712
|
+
kwargs_for_super["metric"] = wrapped_metric
|
|
713
|
+
result = super().__call__(program, **kwargs_for_super)
|
|
714
|
+
|
|
715
|
+
score = result[0] if isinstance(result, tuple) else result
|
|
716
|
+
|
|
717
|
+
if lw_dspy.run_id:
|
|
718
|
+
lw_dspy.log_step(
|
|
719
|
+
optimizer=DSPyOptimizer(name="Evaluate", parameters={}),
|
|
720
|
+
index="0",
|
|
721
|
+
score=float(score),
|
|
722
|
+
label="score",
|
|
723
|
+
predictors=[
|
|
724
|
+
DSPyPredictor(name=name, predictor=predictor)
|
|
725
|
+
for name, predictor in program.named_predictors()
|
|
726
|
+
],
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
return result
|
|
730
|
+
|
|
731
|
+
|
|
661
732
|
# === Tracer ===#
|
|
662
733
|
|
|
663
734
|
|
langwatch/telemetry/tracing.py
CHANGED
|
@@ -267,9 +267,10 @@ class LangWatchTrace:
|
|
|
267
267
|
self,
|
|
268
268
|
):
|
|
269
269
|
ensure_setup()
|
|
270
|
-
|
|
270
|
+
# Making sure it doesn't get mixed up with langwatch.dspy from `import langwatch`
|
|
271
|
+
from langwatch.dspy.__init__ import DSPyTracer
|
|
271
272
|
|
|
272
|
-
|
|
273
|
+
DSPyTracer(trace=self)
|
|
273
274
|
|
|
274
275
|
def share(self) -> str:
|
|
275
276
|
"""Share this trace and get a shareable URL."""
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
langwatch/__init__.py,sha256=nepKSqGG6MQnLgn3QrzcAQ24wlXc-wBAcS0Ks7oVUKk,3681
|
|
2
|
-
langwatch/__version__.py,sha256=
|
|
2
|
+
langwatch/__version__.py,sha256=_Ef6eRJnXlLxv6Pt9FrqxJZdOHx4onlAQg2rPnAnV1E,64
|
|
3
3
|
langwatch/attributes.py,sha256=VsNstyDPaMIjsM4U7yAIDE7ouVxgwhOdbQlrhce9XU8,3699
|
|
4
4
|
langwatch/batch_evaluation.py,sha256=cZ_OxDEpZhwVQap8fub3vcdWVYw9OdUCg7WTuy5FgoY,15748
|
|
5
5
|
langwatch/client.py,sha256=w-WkYRwgFMlfljAwcR6GBT11kBQOc3Ifb3hGY1htOd4,13156
|
|
@@ -15,7 +15,7 @@ langwatch/tracer.py,sha256=t5FOdP1es9H_pPGqGUBLXCyEln0tTi4m4M9b6WxCrPU,975
|
|
|
15
15
|
langwatch/types.py,sha256=DkZThS7ptUY0_Ao7PNO6ZGBE9Qj0YmJf5auRkq2Cphw,2570
|
|
16
16
|
langwatch/dataset/__init__.py,sha256=1vUqnyFymnzcIOTahtsPvKTyAet_q-HTKByRUkuED80,2290
|
|
17
17
|
langwatch/domain/__init__.py,sha256=luuin3-6mmMqDaOJE_1wb7M1ccjhhZL80UN0TBA_TXM,6040
|
|
18
|
-
langwatch/dspy/__init__.py,sha256=
|
|
18
|
+
langwatch/dspy/__init__.py,sha256=E9rqyhOyIO3E-GxJSZlHtsvjapFjKQGF85QRcpKSvKE,34280
|
|
19
19
|
langwatch/evaluation/__init__.py,sha256=Jy7PW5VQbMoDGdOLRlQmDEvo_9TDkBLmrLrfocxddLM,281
|
|
20
20
|
langwatch/evaluation/evaluation.py,sha256=AF3VXCcTGB3F8ChsjwBxqjUXkLzvTbkWbiWYxRzVWik,16037
|
|
21
21
|
langwatch/exporters/filterable_batch_span_exporter.py,sha256=MlhZjui56XD6p2sa8kEGyr-Hb3wqudknngmemnB4Twg,2142
|
|
@@ -207,7 +207,7 @@ langwatch/prompt/prompt.py,sha256=ujcyL_7d0fCLXT-UbFcdnH90D6tkSD5Ro5JGFncU9fQ,19
|
|
|
207
207
|
langwatch/telemetry/context.py,sha256=ixKvTBZADFuWglFCK95r8bF4-f-iq_WixzC4ydirCE8,3888
|
|
208
208
|
langwatch/telemetry/sampling.py,sha256=XDf6ZoXiwpHaHDYd_dDszSqH8_9-CHFNsGAZWOW1VYk,1327
|
|
209
209
|
langwatch/telemetry/span.py,sha256=HKYouwWWHCsc1PtnrB3UFFB-X1LT7DA5cSy-iA5Qoug,32779
|
|
210
|
-
langwatch/telemetry/tracing.py,sha256=
|
|
210
|
+
langwatch/telemetry/tracing.py,sha256=7zPQ1W3KZKi35vzs1veTLviWkRe6bEA4x5_baNsjP2c,26650
|
|
211
211
|
langwatch/telemetry/types.py,sha256=Q9H7nT3GMK1aluRB7CCX8BR7VFKrQY_vdFdyF4Yc98U,501
|
|
212
212
|
langwatch/utils/__init__.py,sha256=3rqQTgzEtmICJW_KSPuLa5q8p5udxt5SRi28Z2vZB10,138
|
|
213
213
|
langwatch/utils/capture.py,sha256=uVKPqHCm-o8CpabsUfhqbNFr5sgUHzcKnBadvL2oIwI,1172
|
|
@@ -216,6 +216,6 @@ langwatch/utils/initialization.py,sha256=LOmGCiox7JRiHprnB04AdO8BhUggZtRbWgBPF9m
|
|
|
216
216
|
langwatch/utils/module.py,sha256=KLBNOK3mA9gCSifCcQX_lOtU48BJQDWvFKtF6NMvwVA,688
|
|
217
217
|
langwatch/utils/transformation.py,sha256=5XUnW7Oz8Ck9EMsKeKeoDOrIw3EXpLGMk_fMSeA0Zng,7216
|
|
218
218
|
langwatch/utils/utils.py,sha256=ZCOSie4o9LdJ7odshNfCNjmgwgQ27ojc5ENqt1rXuSs,596
|
|
219
|
-
langwatch-0.2.
|
|
220
|
-
langwatch-0.2.
|
|
221
|
-
langwatch-0.2.
|
|
219
|
+
langwatch-0.2.9.dist-info/METADATA,sha256=FbRafO8xi9pzIgZXDnJkchcqI6izMMaejU_EznN2KZU,12886
|
|
220
|
+
langwatch-0.2.9.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
221
|
+
langwatch-0.2.9.dist-info/RECORD,,
|
|
File without changes
|