Commit 9e4907f
feat(llmobs): control-plane HTTP client for experiments (#9158)
* spike(llmobs): control-plane HTTP client for experiments
Initial spike toward unifying the standalone Node LLM Experiments SDK
into dd-trace under llmobs. Adds a fetch-based control-plane client
(no new dependencies, same approach as src/aiguard/client.js) that
sources DD_API_KEY / DD_APP_KEY / site from tracer config, resolves the
api.<site> host, and implements get-or-create project as the first
working call. Includes a mocha/sinon spec.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(llmobs): datasets & experiments via tracer.llmobs.experiments
Ports the standalone Node LLM Experiments SDK into dd-trace as an
llmobs submodule, exposed as tracer.llmobs.experiments. No new runtime
dependency — all backend calls go through the global fetch client from
the spike, sourcing DD_API_KEY / DD_APP_KEY / site from tracer config.
- dataset.js / experiment.js / result.js: Dataset + DatasetRecord,
Experiment.run() orchestration with inline span/metric builders,
ExperimentResult/Row.
- index.js: Experiments facade (createDataset, pullDataset with
exponential backoff, experiment()) + createExperiments gating.
- noop.js: NoopExperiments for when llmobs is disabled or keys are
missing; wired into both the LLMObs SDK and its noop.
- index.d.ts: llmobs.Experiments / Dataset / Experiment / ExperimentResult types.
- tests: mocha/sinon specs for client, dataset+experiment run, and the
facade (23 passing); plus a manual end-to-end example script.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(llmobs): cover experiments error paths and branches
Raises patch coverage on the experiments module to ~99.7% lines: adds
tests for dataset getters + DatasetRecord instances, non-array push
response padding, dataset/records/experiment create failures, the
failed-status path when posting events errors, categorical stringify
branches, experiment getters, the client `site` getter, the facade
createDataset/experiment factories, the no-op operations, and
pullDataset list-error and expected-count-not-met errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(llmobs): generate experiment span/trace ids with the tracer's own id module
Reuse the same root-span convention as opentracing/span.js (a single random
64-bit span id, reused as the trace id's low 64 bits with a start-time-derived
high part) instead of a bespoke 32-char hex id shared between span and trace.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(llmobs): mark an experiment failed when any row or evaluation errors
A task/evaluator error is isolated per row (the run doesn't abort), but the
experiment status still reported "completed" even when rows failed. Track
whether any row hit an error and report the experiment as "failed" overall
in that case.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(llmobs): don't drop records pushed mid-flight, report push status
Dataset#push() advanced #pushedCount to the live records length after
awaiting the records POST, so a record added while that POST was in
flight would be silently skipped by the next push. Advance by the
snapshotted pending count instead.
Also read created records from the append-records response's top-level
`records` field (it doesn't use the usual `data` envelope), and resolve
push()/ensureCreatedAndPushed() with { pushedCount, totalCount } so
callers can confirm a push landed fully.
* chore(llmobs): mirror the experiments type surface into index.d.v5.ts
Datasets & Experiments interfaces were added to index.d.ts (v6) but not
yet to the frozen v5 type surface, so v5 consumers had no types for
tracer.llmobs.experiments. Mirror the same interfaces (Dataset,
Experiment, Experiments, DatasetPushResult, etc.) into index.d.v5.ts.
* fix(llmobs): follow pagination when pulling dataset records
pullDataset() only fetched the first page of a dataset's records
endpoint, so a dataset with more records than fit on one page returned
a silently truncated Dataset, and polling for an expectedRecordCount
beyond the first page reread the same page until the wait budget
expired. Follow the response's meta.after cursor via page[cursor]
until the last page.
* fix(llmobs): throw an actionable error when no project name is configured
createExperiments() already falls back to config.service when
llmobs.mlApp isn't set, but if neither is set it silently sent an empty
project name to the backend and failed with a confusing "Failed to
create or get project" error. Gate on it up front and return a
NoopExperiments (consistent with the disabled/missing-keys gates) that
tells the user exactly which env var or tracer.init() option to set
before retrying.
* fix(llmobs): classify evaluator metrics to match dd-trace-py
Object-valued evaluator results were stringified into a single opaque
categorical value instead of using metric_type "json", making them
unreadable in the UI. Mirror dd-trace-py's
_generate_metric_from_evaluation classification: dicts (plain objects)
become json, everything else non-primitive (including arrays) falls
through to categorical with a lowercased string representation.
* fix(llmobs): propagate the underlying error when pulling dataset records fails
pullDataset() previously swallowed a records-fetch error into a
successful empty Dataset whenever expectedRecordCount wasn't set,
masking real API/network failures as an empty dataset.
* chore(llmobs): tighten comments in the experiments module
Drop the stale "Spike:" label, orphaned W1/W2 workaround tags, and
version-specific (v0.1) wording; keep the substance each comment was
actually conveying.
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ruben Bridgewater <ruben@bridgewater.de>1 parent 6f1b5ad commit 9e4907f
14 files changed
Lines changed: 1797 additions & 0 deletions
File tree
- packages/dd-trace
- src/llmobs
- experiments
- test/llmobs/experiments
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3568 | 3568 | | |
3569 | 3569 | | |
3570 | 3570 | | |
| 3571 | + | |
| 3572 | + | |
| 3573 | + | |
| 3574 | + | |
| 3575 | + | |
| 3576 | + | |
3571 | 3577 | | |
3572 | 3578 | | |
3573 | 3579 | | |
| |||
3721 | 3727 | | |
3722 | 3728 | | |
3723 | 3729 | | |
| 3730 | + | |
| 3731 | + | |
| 3732 | + | |
| 3733 | + | |
| 3734 | + | |
| 3735 | + | |
| 3736 | + | |
| 3737 | + | |
| 3738 | + | |
| 3739 | + | |
| 3740 | + | |
| 3741 | + | |
| 3742 | + | |
| 3743 | + | |
| 3744 | + | |
| 3745 | + | |
| 3746 | + | |
| 3747 | + | |
| 3748 | + | |
| 3749 | + | |
| 3750 | + | |
| 3751 | + | |
| 3752 | + | |
| 3753 | + | |
| 3754 | + | |
| 3755 | + | |
| 3756 | + | |
| 3757 | + | |
| 3758 | + | |
| 3759 | + | |
| 3760 | + | |
| 3761 | + | |
| 3762 | + | |
| 3763 | + | |
| 3764 | + | |
| 3765 | + | |
| 3766 | + | |
| 3767 | + | |
| 3768 | + | |
| 3769 | + | |
| 3770 | + | |
| 3771 | + | |
| 3772 | + | |
| 3773 | + | |
| 3774 | + | |
| 3775 | + | |
| 3776 | + | |
| 3777 | + | |
| 3778 | + | |
| 3779 | + | |
| 3780 | + | |
| 3781 | + | |
| 3782 | + | |
| 3783 | + | |
| 3784 | + | |
| 3785 | + | |
| 3786 | + | |
| 3787 | + | |
| 3788 | + | |
| 3789 | + | |
| 3790 | + | |
| 3791 | + | |
| 3792 | + | |
| 3793 | + | |
| 3794 | + | |
| 3795 | + | |
| 3796 | + | |
| 3797 | + | |
| 3798 | + | |
| 3799 | + | |
| 3800 | + | |
| 3801 | + | |
| 3802 | + | |
| 3803 | + | |
| 3804 | + | |
| 3805 | + | |
| 3806 | + | |
| 3807 | + | |
| 3808 | + | |
| 3809 | + | |
| 3810 | + | |
| 3811 | + | |
| 3812 | + | |
| 3813 | + | |
| 3814 | + | |
| 3815 | + | |
| 3816 | + | |
3724 | 3817 | | |
3725 | 3818 | | |
3726 | 3819 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3774 | 3774 | | |
3775 | 3775 | | |
3776 | 3776 | | |
| 3777 | + | |
| 3778 | + | |
| 3779 | + | |
| 3780 | + | |
| 3781 | + | |
| 3782 | + | |
3777 | 3783 | | |
3778 | 3784 | | |
3779 | 3785 | | |
| |||
3918 | 3924 | | |
3919 | 3925 | | |
3920 | 3926 | | |
| 3927 | + | |
| 3928 | + | |
| 3929 | + | |
| 3930 | + | |
| 3931 | + | |
| 3932 | + | |
| 3933 | + | |
| 3934 | + | |
| 3935 | + | |
| 3936 | + | |
| 3937 | + | |
| 3938 | + | |
| 3939 | + | |
| 3940 | + | |
| 3941 | + | |
| 3942 | + | |
| 3943 | + | |
| 3944 | + | |
| 3945 | + | |
| 3946 | + | |
| 3947 | + | |
| 3948 | + | |
| 3949 | + | |
| 3950 | + | |
| 3951 | + | |
| 3952 | + | |
| 3953 | + | |
| 3954 | + | |
| 3955 | + | |
| 3956 | + | |
| 3957 | + | |
| 3958 | + | |
| 3959 | + | |
| 3960 | + | |
| 3961 | + | |
| 3962 | + | |
| 3963 | + | |
| 3964 | + | |
| 3965 | + | |
| 3966 | + | |
| 3967 | + | |
| 3968 | + | |
| 3969 | + | |
| 3970 | + | |
| 3971 | + | |
| 3972 | + | |
| 3973 | + | |
| 3974 | + | |
| 3975 | + | |
| 3976 | + | |
| 3977 | + | |
| 3978 | + | |
| 3979 | + | |
| 3980 | + | |
| 3981 | + | |
| 3982 | + | |
| 3983 | + | |
| 3984 | + | |
| 3985 | + | |
| 3986 | + | |
| 3987 | + | |
| 3988 | + | |
| 3989 | + | |
| 3990 | + | |
| 3991 | + | |
| 3992 | + | |
| 3993 | + | |
| 3994 | + | |
| 3995 | + | |
| 3996 | + | |
| 3997 | + | |
| 3998 | + | |
| 3999 | + | |
| 4000 | + | |
| 4001 | + | |
| 4002 | + | |
| 4003 | + | |
| 4004 | + | |
| 4005 | + | |
| 4006 | + | |
| 4007 | + | |
| 4008 | + | |
| 4009 | + | |
| 4010 | + | |
| 4011 | + | |
| 4012 | + | |
| 4013 | + | |
3921 | 4014 | | |
3922 | 4015 | | |
3923 | 4016 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
0 commit comments