Skip to content

feat(llmobs): add dataset record tags - #9810

Open
mehulsonowal wants to merge 25 commits into
masterfrom
mehul/llmobs-dataset-record-tags-v2
Open

feat(llmobs): add dataset record tags#9810
mehulsonowal wants to merge 25 commits into
masterfrom
mehul/llmobs-dataset-record-tags-v2

Conversation

@mehulsonowal

@mehulsonowal mehulsonowal commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add stable local IDs for dataset records, including records created without a caller-supplied ID.
  • Add dataset record tags, tag-based dataset filtering, and propagation of record tags to experiment spans.
  • Add local dataset update() and delete() operations with batched insert/update/delete/tag mutations.
  • Preserve explicit null values in sparse update payloads and reconcile local changes safely after concurrent pushes.

API interface

const dataset = tracer.llmobs.experiments.createDataset('capitals', {
  records: [{
    inputData: { country: 'France' },
    expectedOutput: 'Paris',
    metadata: { continent: 'Europe' },
    tags: ['split:e2e', 'topic:geography'],
  }],
})

dataset.addRecord(inputData, expectedOutput, metadata, tags)
dataset.addTags(index, ['split:eval'])
dataset.removeTags(index, ['split:holdout'])
dataset.replaceTags(index, ['split:train'])
dataset.update(index, { input, expectedOutput, metadata })
dataset.delete(index)
await dataset.push()

const tagged = await tracer.llmobs.experiments.pullDataset('capitals', {
  tags: ['split:e2e'],
})
tagged.filterTags()

createDataset() record options also accept tags?: string[]. Tag operations and record updates/deletes are kept in local state and sent together through batchUpdateDatasetRecords() on push().

Test plan

dd-trace-js

  • Targeted LLMObs experiment client, facade, experiment, and utility tests: 66 passing.
  • Covers generated and custom record IDs, initial tags, tag mutations, tag filtering, sparse updates, explicit null values, deletes, batching, retries, and concurrent local edits.
  • VCR-backed client and facade interactions are replayable without credentials.

llm-observability examples

The runnable validation scripts are tracked in llm-observability PR #93, on branch feat/nodejs-dataset-operations-tags. The PR extends the dataset example with the dataset operations and tag flows covered by this change.

cd experiments/nodejs
npm install
npm run dataset
npm run validate:all

The dataset example reports initial and generated IDs, initial record tags, addTags(), removeTags(), replaceTags(), tag-filtered pulls, update(), delete(), batched pushes, version changes, follow-up pulls, and experiment execution over a tagged dataset slice. It avoids assertion-heavy output and exits non-zero only for SDK or backend errors. Live validation should be run with Datadog credentials, for example:

dd-auth --domain dd.datadoghq.com -- env DD_SITE=datadoghq.com npm run dataset

This pull request is no longer stacked on #9809; the local dataset refactor has been merged into this branch and the tags work is based on current master.

@dd-octo-sts

dd-octo-sts Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Overall package size

Self size: 8.2 MB
Deduped: 8.87 MB
No deduping: 8.87 MB

Dependency sizes | name | version | self size | total size | |------|---------|-----------|------------| | import-in-the-middle | 3.3.3 | 125.43 kB | 441.72 kB | | opentracing | 0.14.7 | 194.81 kB | 194.81 kB | | dc-polyfill | 0.1.11 | 25.74 kB | 25.74 kB |

🤖 This report was automatically generated by heaviest-objects-in-the-universe

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Aug 13, 2026

Copy link
Copy Markdown

Pipelines  Tests

⚠️ Warnings

🚦 10 Pipeline jobs failed

Project | lint   View in Datadog   GitHub Actions

🔧 Fix in code. This looks caused by changes in this PR. 5 linting issues detected: use 'const' for 'datasetVersion', duplicate names 'update' and 'delete', identical function implementations, and line length of 121 exceeds maximum of 120.

AI Guard | AI Guard / windows   View in Datadog   GitHub Actions

See error Filename too long for the created file during artifact generation.

APM Capabilities | tracing-windows   View in Datadog   GitHub Actions

See error Filename too long: unable to create file 'packages/dd-trace/test/llmobs/cassettes/datadog-experiments/datadog-experiments_api_v2_llm-obs_v1_5645ffb8-c97e-4ce4-89f2-c41931318fd9_datasets_bf7a0d2c-2a6e-4a94-9f2b-9c4b2ad2a8f1_records_filter_version__2_filter_tags__split_eval_get_c3decfe7.json'.

View all 10 failed jobs.

📋 Copy prompt for your agent
CI on my pull request is failing. Help me find and fix the root cause of each failing job below — they were flagged as caused by changes in this PR, so focus on the diff. For each job, explain the failure and propose a fix.

Branch: mehul/llmobs-dataset-record-tags-v2

Project | lint
Commit: 9290c437a8a9f10cb625a6f198c4ebf00a94e596
Error (code / quality):
5 linting issues detected: use 'const' for 'datasetVersion', duplicate names 'update' and 'delete', identical function implementations, and line length of 121 exceeds maximum of 120.
CI job: https://github.com/DataDog/dd-trace-js/actions/runs/31835529268/job/94881445848

ℹ️ Info

No other issues found (see more)

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 54.51%
Overall Coverage: 93.68% (-4.85%)

Useful? React with 👍 / 👎

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 71c9b0d | Docs | Datadog PR Page | Give us feedback!

@pr-commenter

pr-commenter Bot commented Aug 13, 2026

Copy link
Copy Markdown

Benchmarks

Benchmark execution time: 2026-08-14 20:07:02

Comparing candidate commit 71c9b0d in PR branch mehul/llmobs-dataset-record-tags-v2 with baseline commit 9af1c35 in branch master.

📊 Benchmarking dashboard

Found 0 performance improvements and 0 performance regressions! Performance is the same for 2278 metrics, 14 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Unstable benchmarks

These benchmarks have a confidence interval too wide to call a change; treat them as noise rather than signal.

scenario:appsec-iast-with-vulnerability-iast-enabled-default-config-20

  • unstable max_rss_usage [-17.820MB; +21.994MB] or [-4.544%; +5.608%]

scenario:debugger-line-probe-with-snapshot-minimal-24

  • unstable max_rss_usage [-12.517MB; +18.354MB] or [-5.157%; +7.562%]

scenario:debugger-line-probe-without-snapshot-26

  • unstable cpu_user_time [-588.590ms; +923.143ms] or [-3.955%; +6.203%]

scenario:dogstatsd-with-tags-20

  • unstable cpu_user_time [-270.775ms; +349.836ms] or [-5.411%; +6.991%]
  • unstable execution_time [-270.127ms; +356.766ms] or [-5.320%; +7.027%]
  • unstable throughput [-127240.119op/s; +95384.155op/s] or [-7.701%; +5.773%]

scenario:plugin-claude-agent-sdk-compact-stream-scan-26

  • unstable cpu_usage_percentage [-6.962%; +3.629%]

scenario:plugin-graphql-long-with-depth-and-collapse-off-20

  • unstable max_rss_usage [-23.911MB; +30.283MB] or [-6.043%; +7.654%]

scenario:plugin-graphql-long-with-depth-off-20

  • unstable max_rss_usage [-7561.683KB; +6555.969KB] or [-5.791%; +5.020%]

scenario:plugin-memcached-hashring-24

  • unstable cpu_user_time [-1378.468ms; +417.396ms] or [-46.756%; +14.158%]
  • unstable execution_time [-1389.773ms; +420.806ms] or [-47.037%; +14.242%]
  • unstable instructions [-10.0G instructions; +3.2G instructions] or [-26.980%; +8.702%]
  • unstable throughput [-381228.377op/s; +1688095.564op/s] or [-2.579%; +11.421%]

scenario:test-optimization-large-suite-20

  • unstable max_rss_usage [-3.597MB; +6.797MB] or [-4.574%; +8.643%]

@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 54.67626% with 126 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.68%. Comparing base (9af1c35) to head (71c9b0d).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
...ackages/dd-trace/src/llmobs/experiments/dataset.js 42.38% 87 Missing ⚠️
packages/dd-trace/src/llmobs/experiments/noop.js 43.85% 32 Missing ⚠️
packages/dd-trace/src/llmobs/experiments/util.js 81.08% 7 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #9810      +/-   ##
==========================================
- Coverage   98.53%   93.68%   -4.85%     
==========================================
  Files         975      959      -16     
  Lines      144058   141841    -2217     
  Branches    12379    10848    -1531     
==========================================
- Hits       141941   132884    -9057     
- Misses       2117     8957    +6840     
Flag Coverage Δ
aiguard 57.01% <19.78%> (-0.25%) ⬇️
aiguard-integration 55.32% <19.78%> (-0.17%) ⬇️
apm-bucket-0 57.42% <19.78%> (-0.23%) ⬇️
apm-bucket-1 62.60% <19.78%> (-0.24%) ⬇️
apm-bucket-2 61.42% <19.78%> (-0.25%) ⬇️
apm-bucket-3 59.06% <19.78%> (-0.23%) ⬇️
apm-capabilities-tracing 62.25% <19.78%> (-0.08%) ⬇️
apm-integrations-aerospike 55.24% <6.83%> (-0.31%) ⬇️
apm-integrations-confluentinc-kafka-javascript 60.38% <19.78%> (-0.28%) ⬇️
apm-integrations-couchbase 56.01% <19.78%> (-0.23%) ⬇️
apm-integrations-http 61.18% <19.78%> (-0.23%) ⬇️
apm-integrations-kafkajs ?
apm-integrations-next 58.68% <19.78%> (-0.23%) ⬇️
apm-integrations-prisma ?
appsec ?
appsec-_express_fastify 72.65% <19.78%> (?)
appsec-express_fastify_graphql ?
appsec-graphql_kafka_ldapjs 67.48% <19.78%> (?)
appsec-integration ?
appsec-kafka_ldapjs_lodash ?
appsec-lodash_mongodb-core_mongoose 64.24% <19.78%> (?)
appsec-mongodb-core_mongoose_mysql ?
appsec-mysql_node-serialize_passport 65.83% <19.78%> (?)
appsec-next 56.08% <19.78%> (-0.19%) ⬇️
appsec-node-serialize_passport_postgres ?
appsec-sourcing_stripe_template 64.02% <19.78%> (-0.22%) ⬇️
debugger ?
instrumentations-bucket-0 51.38% <19.78%> (+0.11%) ⬆️
instrumentations-bucket-1 58.68% <19.78%> (-0.48%) ⬇️
instrumentations-bucket-10 60.18% <19.78%> (-0.24%) ⬇️
instrumentations-bucket-11 60.91% <19.78%> (-0.16%) ⬇️
instrumentations-bucket-12 51.07% <19.78%> (-0.11%) ⬇️
instrumentations-bucket-13 51.82% <19.78%> (-0.21%) ⬇️
instrumentations-bucket-14 51.02% <19.78%> (-0.26%) ⬇️
instrumentations-bucket-2 52.52% <19.78%> (+<0.01%) ⬆️
instrumentations-bucket-3 52.86% <19.78%> (-0.29%) ⬇️
instrumentations-bucket-4 58.06% <19.78%> (-0.18%) ⬇️
instrumentations-bucket-5 49.56% <19.78%> (+0.71%) ⬆️
instrumentations-bucket-6 59.73% <19.78%> (-0.03%) ⬇️
instrumentations-bucket-7 57.41% <19.78%> (+5.94%) ⬆️
instrumentations-bucket-8 58.40% <19.78%> (+0.46%) ⬆️
instrumentations-bucket-9 56.75% <19.78%> (-0.03%) ⬇️
instrumentations-instrumentation-couchbase 49.96% <6.83%> (-0.29%) ⬇️
instrumentations-integration-esbuild 33.86% <20.79%> (-0.02%) ⬇️
llmobs-ai_anthropic_bedrock ?
llmobs-anthropic_bedrock_claude-agent-sdk 61.55% <19.78%> (?)
llmobs-bucket-1 ?
llmobs-google-genai_langchain_openai-agents 60.79% <19.78%> (?)
llmobs-openai ?
llmobs-openai-agents_vertex-ai ?
llmobs-sdk 67.11% <54.67%> (-0.14%) ⬇️
llmobs-vertex-ai 58.19% <19.78%> (?)
master-coverage 93.68% <54.67%> (?)
openfeature 54.93% <19.78%> (-0.27%) ⬇️
openfeature-unit 52.66% <19.78%> (-0.21%) ⬇️
platform-core_esbuild_instrumentations-misc 40.79% <19.78%> (-0.08%) ⬇️
platform-integration 59.73% <19.78%> (-0.20%) ⬇️
platform-shimmer_unit-guardrails_webpack 38.44% <19.78%> (-0.07%) ⬇️
plugins-bucket-0 56.25% <19.78%> (-0.20%) ⬇️
plugins-bucket-1 53.37% <19.78%> (-0.19%) ⬇️
plugins-bucket-10 61.19% <19.78%> (?)
plugins-bucket-11 ?
plugins-bucket-16 60.54% <19.78%> (?)
plugins-bucket-17 61.22% <19.78%> (+0.44%) ⬆️
plugins-bucket-18 60.57% <19.78%> (-0.88%) ⬇️
plugins-bucket-19 62.93% <19.78%> (+2.12%) ⬆️
plugins-bucket-20 ?
plugins-bucket-4 57.61% <19.78%> (-0.23%) ⬇️
plugins-bullmq_cassandra_cookie 60.62% <19.78%> (-0.24%) ⬇️
plugins-cookie-parser_crypto_dd-trace-api 55.65% <19.78%> (-0.23%) ⬇️
plugins-fetch_fs_generic-pool 57.56% <19.78%> (-0.22%) ⬇️
plugins-google-cloud-pubsub_grpc_handlebars ?
plugins-grpc_handlebars_hapi 60.48% <19.78%> (?)
plugins-hapi_hono_ioredis ?
plugins-hono_ioredis_langgraph 58.61% <19.78%> (?)
plugins-knex_langgraph_ldapjs ?
plugins-ldapjs_light-my-request_limitd-client 57.54% <19.78%> (?)
plugins-light-my-request_limitd-client_lodash ?
plugins-lodash_mariadb_memcached 57.25% <19.78%> (?)
plugins-mariadb_memcached_mercurius ?
plugins-moleculer_mongodb-core_mongoose 60.45% <19.78%> (?)
plugins-mongodb_mongodb-core_mongoose ?
plugins-multer_mysql_mysql2 58.13% <19.78%> (-0.23%) ⬇️
plugins-nats_node-serialize_opensearch 59.65% <19.78%> (-0.24%) ⬇️
plugins-passport-http_pino_postgres 57.88% <19.78%> (-0.23%) ⬇️
plugins-process_pug_redis 56.68% <19.78%> (-0.23%) ⬇️
plugins-undici_url_valkey 57.35% <19.78%> (-0.18%) ⬇️
plugins-vm_winston_ws 58.86% <19.78%> (-0.24%) ⬇️
profiling 60.82% <19.78%> (-0.36%) ⬇️
serverless-aws-sdk-aws-sdk 54.24% <19.78%> (-0.18%) ⬇️
serverless-aws-sdk-base-inject-field 50.30% <19.78%> (-0.20%) ⬇️
serverless-aws-sdk-bedrockruntime 54.02% <19.78%> (-0.19%) ⬇️
serverless-aws-sdk-client 55.52% <19.78%> (-0.21%) ⬇️
serverless-aws-sdk-dynamodb 54.84% <19.78%> (-0.19%) ⬇️
serverless-aws-sdk-eventbridge 56.32% <19.78%> (-0.19%) ⬇️
serverless-aws-sdk-kinesis ?
serverless-aws-sdk-lambda 56.54% <19.78%> (-0.21%) ⬇️
serverless-aws-sdk-s3 54.93% <19.78%> (-0.20%) ⬇️
serverless-aws-sdk-serverless-peer-service 58.93% <19.78%> (-0.22%) ⬇️
serverless-aws-sdk-sns 59.14% <19.78%> (-0.22%) ⬇️
serverless-aws-sdk-sqs 59.55% <19.78%> (-0.22%) ⬇️
serverless-aws-sdk-stepfunctions 54.76% <19.78%> (-0.20%) ⬇️
serverless-aws-sdk-util 50.81% <19.78%> (-0.21%) ⬇️
serverless-azure-functions-cosmosdb_lambda 56.17% <19.78%> (?)
serverless-bucket-0 59.83% <19.78%> (+6.48%) ⬆️
serverless-bucket-1 ?
test-optimization-cucumber 63.76% <19.78%> (-6.79%) ⬇️
test-optimization-cypress 62.76% <19.78%> (-1.92%) ⬇️
test-optimization-jest 70.69% <19.78%> (-1.30%) ⬇️
test-optimization-mocha 64.91% <19.78%> (-7.09%) ⬇️
test-optimization-playwright-playwright-atr 59.42% <19.78%> (-0.09%) ⬇️
test-optimization-playwright-playwright-efd 59.25% <19.78%> (-0.41%) ⬇️
test-optimization-playwright-playwright-final-status 59.67% <19.78%> (-0.16%) ⬇️
test-optimization-playwright-playwright-impacted-tests 59.05% <19.78%> (-0.12%) ⬇️
test-optimization-playwright-playwright-reporting 60.46% <19.78%> (-0.44%) ⬇️
test-optimization-playwright-playwright-test-management 59.82% <19.78%> (-0.59%) ⬇️
test-optimization-playwright-playwright-test-span 59.40% <19.78%> (-0.22%) ⬇️
test-optimization-selenium 58.62% <19.78%> (-0.27%) ⬇️
test-optimization-testopt ?
test-optimization-vitest 67.17% <19.78%> (-5.49%) ⬇️
test-optimization-vitest-browser 57.65% <6.83%> (-0.96%) ⬇️
test-optimization-webdriverio ?

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mehulsonowal
mehulsonowal force-pushed the mehul/llmobs-dataset-record-tags-v2 branch from 7f7f5fa to f1e0c3b Compare August 13, 2026 21:58
Base automatically changed from mehul/llmobs-dataset-local-state-v2 to master August 13, 2026 22:04
@mehulsonowal
mehulsonowal marked this pull request as ready for review August 14, 2026 15:52
@mehulsonowal
mehulsonowal requested review from a team as code owners August 14, 2026 15:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f916375225

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

if (!Array.isArray(tags)) return result
for (const tag of tags) {
const separator = tag.indexOf(':')
if (separator > 0) result[tag.slice(0, separator)] = tag.slice(separator + 1)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve all same-key record tags

When a dataset record has multiple tags with the same key, which the new string[] tag API allows (for example topic:math and topic:logic), this assignment overwrites the earlier value. Both the LLMObs annotate path and the fallback toSpan() path use this helper, so experiment spans only carry the last record tag and cannot be filtered or grouped by the full record tag set. Please preserve repeated keys, or append the original record tag strings, instead of collapsing them into one object property.

Useful? React with 👍 / 👎.

Comment on lines +269 to +273
this.#pendingTagOperations.delete(recordId)
const update = this.#updatedRecordsById.get(recordId)
if (update) {
delete update.tagOperations
if (Object.keys(update).length === 1) this.#updatedRecordsById.delete(recordId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep inverse tag edits queued during in-flight pushes

If an existing record tagged split:eval is pushed after removeTags(0, ['split:eval']) and the caller re-adds that tag before the push resolves, mergeTagOperations() returns empty and this branch drops the current update. After the in-flight batch commits the removal, #clearCommittedChanges() has no pending update to compare, so no compensating add is sent and the local dataset says the tag exists while the backend record has lost it. Please retain enough state to reconcile against the in-flight payload instead of deleting the pending tag operation outright.

Useful? React with 👍 / 👎.

return output
}

function serializedTagOperations (operations) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to serialize methods

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thats the api interface for tags: https://github.com/DataDog/dd-source/pull/320916

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 71c9b0dcd2

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

records,
datasetVersion,
latestVersion
datasetVersion ?? latestVersion,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep pulled records and dataset version on the same snapshot

When pullDataset() is called without an explicit version while dataset metadata lags ingestion—the exact case this change is handling—the unversioned records request can return the newest snapshot, but this fallback still assigns the older metadata latestVersion to the returned dataset. Experiment.run() subsequently sends that value as dataset_version, associating the experiment with an older snapshot while executing the newer records; resolve the version of the records response or leave it unset instead of substituting stale metadata.

Useful? React with 👍 / 👎.

input: record.inputData,
expectedOutput: record.expectedOutput ?? null,
metadata: record.metadata ?? {},
...(record.tags === undefined ? {} : { tags: record.tags }),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Always populate tags on no-op records

When LLMObs is disabled or credentials are missing and the caller omits the optional tags argument, this conditional leaves record.tags undefined, whereas enabled datasets always expose tags: [] and the public Dataset.records() type declares tags: string[]. Code that safely iterates record.tags in enabled mode can therefore throw only in the no-op path; initialize it to a copied empty array so disabling the subsystem remains graceful.

AGENTS.md reference: AGENTS.md:L222-L225

Useful? React with 👍 / 👎.

if (Object.hasOwn(operations, 'replace')) {
const replaced = new Set(operations.replace)
for (const tag of tags) {
if (operation === 'add') replaced.add(tag)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the op can only be add or delete at this point?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants