Skip to content

[DRAFT] Add SDK v2 telemetry core#879

Draft
bmehta001 wants to merge 32 commits into
mainfrom
bhamehta/flcore/1ds-telemetry-core
Draft

[DRAFT] Add SDK v2 telemetry core#879
bmehta001 wants to merge 32 commits into
mainfrom
bhamehta/flcore/1ds-telemetry-core

Conversation

@bmehta001

Copy link
Copy Markdown
Contributor

Summary

  • Introduces the core SDK v2 telemetry abstractions and typed telemetry payloads.
  • Wires 1DS upload plumbing, invocation context/correlation IDs, model/session/action events, catalog/download/EP telemetry, and app session IDs.

Notes

  • Draft split from the larger telemetry branch.
  • Follow-up hardening/packaging fixes are stacked separately.

bmehta001 and others added 11 commits July 9, 2026 11:37
Port the C# Foundry Core telemetry event taxonomy to C++ in advance of the
1DS bridge.  The interface refactor is the source of truth — all backend
implementations and call sites are wired against it.

ITelemetry additions:
  - 4 new `Action` values (EpDownloadAttempt, EpDownloadAndRegister,
    ModelFileDownload, ModelInference).
  - 4 new payload structs (`EpDownloadAttemptInfo`,
    `EpDownloadAndRegisterInfo`, `ModelUsageInfo`, `DownloadInfo`).
  - 3 new virtual methods (`RecordEpDownloadAttempt`,
    `RecordEpDownloadAndRegister`, `RecordDownload`).
  - Replaced `RecordModelUsage(model_id, prompt_tokens, completion_tokens,
    duration_ms)` with `RecordModelUsage(const ModelUsageInfo&)` so callers
    can populate richer fields (TimeToFirstToken, EP, memory).
  - Extended `RecordModelId(action, model_id)` to take `status` and
    `user_agent`; added `RecordException(action, ex, user_agent)` overload.

New supporting code (all in `sdk_v2/cpp/src/telemetry/`):
  - `telemetry_environment.{h,cc}` — `IsCiEnvironment` /
    `IsTestingMode` / `IsTruthyValue` / `GetEnv`.  13 CI env-var
    names ported verbatim from neutron-server's `TelemetryEnvironment.cs`.
  - `telemetry_metadata.{h,cc}` — captures per-process metadata
    (`app_session_guid`, version, os_name / os_version / cpu_arch,
    test_mode flag) for stamping on every event.
  - `ep_download_tracker.{h,cc}` — RAII tracker that emits one
    `EPDownloadAndRegister` event per bootstrapper, recording stage
    transitions (initial -> download -> register).  Default failure
    semantics: dtor records remaining stages as `kFailure`; `Done()`
    records them as `kSkipped` for happy-path early exit.
  - `download_tracker.{h,cc}` — RAII tracker that emits one `Download`
    event per `DownloadManager::DownloadModel` call.

`TelemetryLogger` (the fallback / mirror) implements every new method,
formatting events as `[Telemetry] EventName Field=value ...` at Debug.
Test stubs (`NullTelemetry` and the `RecordingTelemetry` used by
`telemetry_test.cc`) were updated for the new interface.

Build verified RelWithDebInfo --no_telemetry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
OneDsTelemetry is the production ITelemetry implementation.  It embeds a
TelemetryLogger mirror so every event is also logged locally to ILogger for
diagnostics, then conditionally uploads to 1DS through the Microsoft
cpp-client-telemetry SDK.

Suppression model (three-state):
  - CI environment detected -> skip `LogManager::Initialize`; local logging
    still happens, but no upload occurs.
  - Tenant token empty (build did not pass `-DFOUNDRY_LOCAL_TELEMETRY_TOKEN`)
    -> same as CI: no upload, only local logging.
  - Otherwise -> upload.  Every event is stamped with `test=true` when
    `FOUNDRY_TESTING_MODE` is truthy, `test=false` otherwise.

Common context, propagated to every event via `ILogger::SetContext`:
  - `app_name` (from `Configuration::app_name`).
  - `app_session_guid` (random v4 UUID; on Windows the special
    `UTCReplace_AppSessionGuid` field also gets the OS app session GUID).
  - `version` (from the generated `version.h`).
  - `os_name` / `os_version` / `cpu_arch` (Win: `GetVersionExA` +
    `GetNativeSystemInfo`; POSIX: `uname`).

The `MICROSOFT_KEYWORD_CRITICAL_DATA` (bit 47) policy flag is set on
every event via `SetPolicyBitFlags` so the data passes 1DS classifier.

Build / packaging:
  - `vcpkg.json`: `telemetry` feature pulls `cpp-client-telemetry`.
    The port is pending in microsoft/vcpkg#52316; until that merges,
    builds must pass `--no_telemetry`.
  - `CMakeLists.txt`: new `FOUNDRY_LOCAL_USE_TELEMETRY` option
    (default ON) and `FOUNDRY_LOCAL_TELEMETRY_TOKEN` advanced cache
    variable.  Calls `find_package(MSTelemetry CONFIG REQUIRED)`, includes
    `one_ds_telemetry.cc` and `configure_file`-generates
    `one_ds_tenant_token.h` containing the build-time token, then links
    `MSTelemetry::mat` and defines `FOUNDRY_LOCAL_HAS_1DS=1`.
  - `build.py`: new `--no_telemetry` and `--telemetry_token` flags;
    forwards through to CMake / VCPKG_MANIFEST_FEATURES.

Build verified RelWithDebInfo --no_telemetry; --use_telemetry will be
validated once the vcpkg port lands.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Plumb the typed telemetry interface through the orchestration layer so
that real workloads produce the new EPDownloadAttempt, EPDownloadAndRegister
and Download events.

Manager:
  - Construct `telemetry_` before `ep_detector_` so the detector can be
    handed a non-null sink at ctor time.  When `FOUNDRY_LOCAL_HAS_1DS` is
    defined, the implementation is `OneDsTelemetry` and reads the
    build-time token from the generated `one_ds_tenant_token.h`; otherwise
    it falls back to `TelemetryLogger`.
  - Member-declaration order in `manager.h` reorganised so consumers
    (`ep_detector_`, `download_manager_`) appear after the providers
    (`telemetry_`).  C++ destroys in reverse declaration order, so this
    keeps the raw `ITelemetry*` held by `EpDetector` and
    `DownloadManager` valid for their entire lifetime.
  - Explicit `~Manager()` Shutdown reset order updated to match: the
    telemetry sink is reset last (after ep_detector and download_manager).

EpDetector:
  - New optional `ITelemetry* telemetry` ctor arg (default nullptr) so
    unit tests that instantiate EpDetector directly continue to compile.
  - `DownloadAndRegisterEps` emits one `EPDownloadAndRegister` event
    per bootstrapper via `EpDownloadTracker` and a single aggregate
    `EPDownloadAttempt` event at the end with attempt / success / fail
    counts and an overall status.
  - Exceptions thrown from a bootstrapper invoke
    `EpDownloadTracker::RecordException` before re-throwing so the failure
    is recorded regardless of how it propagates.

DownloadManager:
  - New optional `ITelemetry* telemetry` ctor arg.  When non-null,
    `DownloadModel` wraps the call with a `DownloadTracker` that
    captures lock-wait, enumeration and download timings, total bytes,
    file count, max concurrency and final status (Success / Skipped /
    Failure).  `RecordException` is invoked on the exception path before
    re-throwing.
  - `DownloadModel` now takes an optional `user_agent` parameter so
    HTTP-driven downloads can attribute the event to the calling client.

DownloadBlobsToDirectory:
  - New optional `BlobDownloadStats*` out parameter populated with
    `total_size_bytes`, `file_count`, `enumeration_ms`,
    `download_ms`.  All existing 4-argument callers (including unit
    tests) keep working because the parameter defaults to nullptr.

Verified RelWithDebInfo --no_telemetry: 756 tests pass; 54 skipped
(environmental, missing local test data); 0 functional failures.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Now that microsoft/vcpkg#52316 has merged, bump the manifest baseline
from 256acc64 to 44819aa2 (current master) so the cpp-client-telemetry
3.10.161.1 port resolves.

Also rename the using-declaration in one_ds_telemetry.cc's anonymous
namespace from `using ::Microsoft::Applications::Events::ILogger;` to
`using MatILogger = ::Microsoft::Applications::Events::ILogger;` so it
no longer collides with the local `fl::ILogger` interface from
src/logger.h. Three callsites (SetCommonContext, SafeLog, GetMatLogger)
updated; the OneDsTelemetry ctor's `ILogger& logger` parameter
correctly resolves to fl::ILogger after the rename.

Verified with: python sdk_v2/cpp/build.py --use_telemetry
                       --config RelWithDebInfo --skip_examples
foundry_local.dll = 12.19 MB (telemetry on, empty token).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add /OPT:REF, /OPT:ICF, and /INCREMENTAL:NO to the foundry_local shared
library target for Release and RelWithDebInfo configs. MSVC's /DEBUG flag
(needed for PDB generation) silently disables these optimizations unless
they are explicitly re-enabled, leaving all unreferenced symbols from
statically-linked dependencies (notably cpp-client-telemetry/mat.lib)
in the final binary.

Also add -Wl,--gc-sections (Linux) and -Wl,-dead_strip (macOS) equivalents
for non-Windows builds.

Additionally, declare sqlite3 with default-features:false in vcpkg.json to
drop the unused json1 extension from the SQLite transitive dependency.

Measured impact (RelWithDebInfo, x64-windows, telemetry ON):
  foundry_local.dll: 12.19 MB -> 4.51 MB (-7.68 MB, -63%)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…t) foundation

Reworks the telemetry core ahead of broadening route/inference coverage:

- New InvocationContext {user_agent, correlation_id, indirect} threaded through
  the ITelemetry interface in place of the loose user_agent/indirect params.
  Direct() mints a fresh correlation id; AsIndirect() derives a caused-by child
  that reuses it. ActionTracker now carries the context and guarantees an id.
- Every event (Action/Error/Model/ModelId/Download/EP*) now carries a
  CorrelationId so all events from one operation can be grouped. The Model event
  also gains Stream and Direct.
- indirect now means "happened as a consequence of another action": the
  per-provider EPDownloadAndRegister is marked indirect and shares the overall
  EPDownloadAttempt's correlation id.
- Add ActionStatus::kClientError to separate 4xx client rejects from 5xx/internal
  failures (wired into handlers in a follow-up).
- Prune dead Action enum entries (kModelDownload/kModelDelete/kCoreAudioTranscribe)
  and add kServiceRequestUnmatched for the upcoming router catch-all.
- Share the v4 UUID generator (MakeGuidV4Hex) between metadata and correlation.

Builds clean (/W4 /WX); telemetry + ActionTracker unit tests pass.
…to-end

- Session::ProcessRequest now emits the previously-dead Model (kModelInference)
  event once per inference with model id, tokens, duration, stream flag and the
  caller's correlation id / indirect flag. Adds Session::SetRequestContext so an
  HTTP route can stage an indirect child context (shared correlation id), and a
  virtual ExecutionProvider() hook for the EP field.
- Chat completions handler: derive a Direct route context from the User-Agent
  header; map 4xx early-returns (empty body, invalid JSON, model not found/loaded)
  to kClientError instead of the default kFailure; emit kSessionCreate (indirect)
  on the HTTP path; stage the indirect session context.
- Streaming fix: the route action is no longer recorded (as a premature success
  with ~0ms) when the handler returns. The route ActionTracker is moved into the
  streaming thread and records on completion with the real duration and terminal
  status, so mid-stream failures surface as failures instead of vanishing.

Builds clean (/W4 /WX); telemetry/webservice/sse tests pass.
…, sampling)

Extends the chat-route pattern to every handler:

- Streaming completion fix also applied to the audio transcriptions route (its
  ActionTracker moved into the streaming thread).
- Embeddings, audio, responses (create + get/list/delete/input_items), and the
  model load/unload/list/retrieve routes now derive a Direct context from the
  User-Agent header, map 4xx early-returns to kClientError, and (for the
  inference routes) emit kSessionCreate and stage an indirect session context.
- Instrument GET /models/loaded (kModelList), previously untracked.
- Sample GET /status: emit a kServiceStatus action at most once per hour per
  process so the orchestrator heartbeat doesn't dominate volume.

Builds clean (/W4 /WX); telemetry/webservice/sse tests pass.
…ests

- UnmatchedRouteInterceptor: a request interceptor that, before routing, checks
  the router for a matching route. When none matches (unknown path or wrong
  method) it records a kServiceRequestUnmatched action (kClientError) and replies
  404, so requests that reach the service but no handler are no longer invisible.
- Tests (WebServiceTelemetryTest, using a capturing ITelemetry + empty catalog,
  no real model required):
    * unmatched route records kServiceRequestUnmatched with the right status,
      user agent, Direct flag and a correlation id;
    * an empty chat-completions body records kClientError (not kFailure) and
      emits no Model event;
    * three rapid GET /status calls record kServiceStatus exactly once (hourly
      sampling).

Builds clean (/W4 /WX); 72 telemetry/webservice/sse tests pass.
Every access to a model catalog source is now recorded, with success/failure:

- New CatalogFetch typed event carrying operation ("FetchAll" or the cached-id
  "FetchByIds" lookup), endpoint/region/format parsed from the catalog URL,
  status, duration, model count, error message, and a correlation id shared
  across the accesses of one refresh.
- FetchAllModelInfosWithCachedModels gains optional telemetry params (defaulted,
  so the snapshot tool and existing tests are unchanged) and emits an event for
  the primary fetch and, when it runs, the secondary cached-id lookup.
- AzureModelCatalog parses the catalog URL into endpoint/region/format
  (https://ai.azure.com/api/eastus/ux/v1.0 -> {ai.azure.com, eastus, ux/v1.0};
  "static" for the embedded snapshot) and threads an ITelemetry from Manager.
- Tests assert the primary and secondary accesses are each tracked with the
  right operation, status, endpoint, correlation id and model count.

Builds clean (/W4 /WX); catalog + telemetry + webservice tests pass.
…ions

- Drop the UTCReplace_ magic from the per-process correlation GUID: it only
  fires on the Windows UTC transmission path (not our direct upload, and never
  off-Windows), so it was dead weight. The field is now plainly "AppSessionGuid"
  — a stable per-process correlation id on every platform.
- Add ITelemetry::StartSession()/EndSession() (default no-op; OneDsTelemetry maps
  them to 1DS LogSession(Started/Ended), TelemetryLogger logs them). Manager
  opens a session when the web service starts and closes it on stop, so events
  carry the standard, cross-platform usage-session id (ext.app.sesId) and the
  backend gets session duration. This is additive — it complements the per-run
  AppSessionGuid and the per-operation CorrelationId.

Builds clean (/W4 /WX); telemetry/webservice/catalog tests pass.
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
foundry-local Ready Ready Preview, Comment Jul 14, 2026 5:05pm

Request Review

Copilot AI left a comment

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.

Pull request overview

Adds SDK v2 telemetry infrastructure, 1DS transport, correlation contexts, and instrumentation across inference, downloads, catalogs, EP registration, and HTTP services.

Changes:

  • Introduces typed telemetry payloads, trackers, metadata, and optional 1DS upload support.
  • Instruments SDK operations and streaming HTTP routes with correlated telemetry.
  • Adds telemetry dependencies, build options, tests, and linker-size optimizations.

Reviewed changes

Copilot reviewed 49 out of 49 changed files in this pull request and generated 20 comments.

Show a summary per file
File Description
sdk_v2/cpp/vcpkg.json Adds telemetry dependencies and feature.
sdk_v2/cpp/test/internal_api/web_service_test.cc Tests HTTP telemetry behavior.
sdk_v2/cpp/test/internal_api/telemetry_test.cc Updates telemetry unit tests.
sdk_v2/cpp/test/internal_api/null_telemetry.h Updates no-op test sink.
sdk_v2/cpp/src/telemetry/telemetry.h Defines telemetry API and payloads.
sdk_v2/cpp/src/telemetry/telemetry.cc Maps actions and statuses.
sdk_v2/cpp/src/telemetry/telemetry_metadata.h Declares process metadata.
sdk_v2/cpp/src/telemetry/telemetry_metadata.cc Collects platform metadata.
sdk_v2/cpp/src/telemetry/telemetry_logger.h Expands local telemetry logger.
sdk_v2/cpp/src/telemetry/telemetry_logger.cc Formats typed telemetry events.
sdk_v2/cpp/src/telemetry/telemetry_environment.h Declares runtime telemetry gating.
sdk_v2/cpp/src/telemetry/telemetry_environment.cc Implements CI/test detection.
sdk_v2/cpp/src/telemetry/telemetry_action_tracker.h Adds invocation-context tracking.
sdk_v2/cpp/src/telemetry/telemetry_action_tracker.cc Emits correlated action events.
sdk_v2/cpp/src/telemetry/one_ds_tenant_token.h.in Generates the tenant-token header.
sdk_v2/cpp/src/telemetry/one_ds_telemetry.h Declares the 1DS sink.
sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc Implements 1DS event uploads.
sdk_v2/cpp/src/telemetry/invocation_context.h Defines correlation context.
sdk_v2/cpp/src/telemetry/invocation_context.cc Generates correlation UUIDs.
sdk_v2/cpp/src/telemetry/ep_download_tracker.h Declares EP telemetry tracker.
sdk_v2/cpp/src/telemetry/ep_download_tracker.cc Emits EP attempt details.
sdk_v2/cpp/src/telemetry/download_tracker.h Declares model-download tracker.
sdk_v2/cpp/src/telemetry/download_tracker.cc Emits model-download telemetry.
sdk_v2/cpp/src/service/web_service.cc Instruments status and unmatched routes.
sdk_v2/cpp/src/service/responses_handler.h Passes streaming route trackers.
sdk_v2/cpp/src/service/responses_handler.cc Instruments Responses API operations.
sdk_v2/cpp/src/service/models_handlers.cc Instruments model endpoints.
sdk_v2/cpp/src/service/handler_utils.h Extracts request user agents.
sdk_v2/cpp/src/service/embeddings_handler.cc Instruments embedding inference.
sdk_v2/cpp/src/service/chat_completions_handler.h Extends streaming tracker ownership.
sdk_v2/cpp/src/service/chat_completions_handler.cc Instruments chat streaming.
sdk_v2/cpp/src/service/audio_transcriptions_handler.h Extends audio tracker ownership.
sdk_v2/cpp/src/service/audio_transcriptions_handler.cc Instruments audio streaming.
sdk_v2/cpp/src/manager.h Adjusts telemetry lifetime ordering.
sdk_v2/cpp/src/manager.cc Constructs sinks and manages sessions.
sdk_v2/cpp/src/inferencing/session/session.h Adds per-request telemetry context.
sdk_v2/cpp/src/inferencing/session/session.cc Emits inference usage metrics.
sdk_v2/cpp/src/ep_detection/ep_detector.h Accepts an optional telemetry sink.
sdk_v2/cpp/src/ep_detection/ep_detector.cc Instruments EP registration.
sdk_v2/cpp/src/download/download_manager.h Extends download telemetry API.
sdk_v2/cpp/src/download/download_manager.cc Instruments model downloads.
sdk_v2/cpp/src/download/blob_downloader.h Defines download statistics.
sdk_v2/cpp/src/download/blob_downloader.cc Collects transfer statistics.
sdk_v2/cpp/src/catalog/catalog_client.h Extends catalog telemetry API.
sdk_v2/cpp/src/catalog/catalog_client.cc Emits catalog-fetch events.
sdk_v2/cpp/src/catalog/azure_model_catalog.h Stores the telemetry sink.
sdk_v2/cpp/src/catalog/azure_model_catalog.cc Adds catalog URL dimensions.
sdk_v2/cpp/CMakeLists.txt Configures 1DS and linker stripping.
sdk_v2/cpp/build.py Adds telemetry build options.
Comments suppressed due to low confidence (1)

sdk_v2/cpp/src/service/responses_handler.cc:605

  • Calling Remove() makes shutdown stop waiting for this worker, but the captured route_tracker is destroyed only after the lambda returns. Manager teardown can consequently destroy telemetry in that gap and the tracker destructor will dereference freed telemetry. Emit/destroy the tracker before untracking the thread.
    // Terminal event per spec
    body_ptr->Push("data: [DONE]\n\n");
    body_ptr->Finish();

    tracker.Remove(std::this_thread::get_id());

Comment thread sdk_v2/cpp/build.py Outdated
Comment on lines +481 to +482
if args.telemetry_token is not None:
command += [f"-DFOUNDRY_LOCAL_TELEMETRY_TOKEN={args.telemetry_token}"]
Comment on lines +26 to +30
// GetVersionExA is deprecated and lies for unmanifested apps. The reliable
// approach is to read the build number directly from kernel32 via
// RtlGetVersion, or fall back to the OS version registry. For now, use
// GetVersionEx — the deprecation only affects apps without a manifest, and
// Foundry Local has a manifest declaring Win10 / Win11 compat.
Comment on lines +215 to +217
// Re-throw to preserve existing semantics — the wrapper RAII guard above
// resets download_in_progress_; the tracker dtor records the EP event.
throw;
Comment on lines +231 to +233
if (tracker) {
tracker->RecordDownloadComplete(ActionStatus::kSuccess, "Installed");
tracker->RecordRegisterComplete(ActionStatus::kSuccess, "Registered");
Comment on lines +263 to +268
std::unique_ptr<DownloadTracker> tracker;
if (telemetry_ != nullptr) {
tracker = std::make_unique<DownloadTracker>(info.model_id, user_agent, *telemetry_);
tracker->SetLockWaitMs(lock_wait_ms);
tracker->SetMaxConcurrency(static_cast<int32_t>(max_concurrency_));
}
Comment on lines 251 to +252
body_ptr->Finish();
tracker.Remove(std::this_thread::get_id());
thread_tracker.Remove(std::this_thread::get_id());
Comment on lines +23 to +26
telemetry_.RecordAction(action_, status_, context_, duration_ms);

if (!model_id_.empty()) {
telemetry_.RecordModelId(action_, model_id_);
telemetry_.RecordModelId(action_, model_id_, status_, context_);
Comment on lines +20 to +24
DownloadTracker::~DownloadTracker() {
// Emit the Download event regardless of outcome. The default status is
// kFailure so abrupt exits (exceptions) are recorded as failures.
telemetry_.RecordDownload(info_);
}
Comment on lines +30 to +35
EpDownloadTracker::~EpDownloadTracker() {
// Mirror neutron-server: if the caller didn't reach Done() or
// RecordRegisterComplete, assume the abrupt exit was an exception path and
// record any unfinished stage as kFailure.
RecordEvent(ActionStatus::kFailure);
}
Comment thread sdk_v2/cpp/build.py Outdated
Comment on lines +168 to +174
parser.add_argument(
"--no_telemetry", action="store_true",
help="Skip building the 1DS (cpp-client-telemetry) bridge. Useful while the vcpkg "
"port (microsoft/vcpkg#52316) is unmerged or when building forks that should "
"not link any telemetry transport. Local diagnostic logging via TelemetryLogger "
"still works.",
)
Remove CLI tenant-token plumbing, use manifest-independent Windows OS metadata, make telemetry trackers best-effort, improve EP/download/session telemetry accuracy, and guard moved stream route trackers.

Files changed:

- sdk_v2/cpp/build.py

- sdk_v2/cpp/CMakeLists.txt

- sdk_v2/cpp/src/telemetry/*

- sdk_v2/cpp/src/ep_detection/ep_detector.cc

- sdk_v2/cpp/src/download/*

- sdk_v2/cpp/src/inferencing/session/session.cc

- sdk_v2/cpp/src/service/*handler.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
@bmehta001 bmehta001 requested a review from Copilot July 13, 2026 20:16
bmehta001 added a commit that referenced this pull request Jul 13, 2026
Bring PR #879's Copilot round-1 telemetry fixes into the stacked hardening PR while preserving the hardening branch's broader implementations for conflicting files.

Files changed:

- sdk_v2/cpp/src/download/download_manager.cc

- sdk_v2/cpp/src/ep_detection/ep_detector.cc

- sdk_v2/cpp/src/inferencing/session/session.cc

- sdk_v2/cpp/src/telemetry/download_tracker.h

- sdk_v2/cpp/src/telemetry/telemetry_metadata.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated 13 comments.

.count();
usage.total_tokens = static_cast<int32_t>(response.usage.total_tokens);
usage.input_token_count = static_cast<int32_t>(response.usage.prompt_tokens);
telemetry_.RecordModelUsage(usage);
info.duration_ms = duration_ms;
info.model_count = model_count;
info.error_message = error;
telemetry->RecordCatalogFetch(info);
attempt_info.duration_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - attempt_start)
.count();
telemetry_->RecordEpDownloadAttempt(attempt_info);
Comment thread sdk_v2/cpp/src/manager.cc Outdated
web_service_running_ = true;
// Open an app-usage session for the lifetime of the running service so events
// carry ext.app.sesId and the backend gets session duration.
telemetry_->StartSession();
Comment thread sdk_v2/cpp/src/manager.cc Outdated
web_service_.reset();
web_service_running_ = false;
bound_urls_.clear();
telemetry_->EndSession();
Comment thread sdk_v2/cpp/src/telemetry/telemetry.h Outdated
std::string correlation_id;
bool stream = false; // True if the inference was streamed (SSE) vs a single response
bool indirect = false; // True if the inference was driven by another action (e.g. an HTTP route)
int64_t time_to_first_token_ms = 0;
Comment on lines +256 to +258
int64_t lock_wait_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
clock::now() - lock_wait_start)
.count();
Comment on lines +46 to +50
if (auto slash = rest.find('/'); slash == std::string::npos) {
out.endpoint = rest;
} else {
out.endpoint = rest.substr(0, slash);
path = rest.substr(slash + 1);
Comment thread sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc
Comment thread sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc
Make telemetry sink calls best-effort across model/catalog/session paths, sanitize 1DS error text, mirror session events locally, use an unknown TTFT sentinel, improve catalog URL dimensions and status sampling, and measure download lock waits accurately.

Files changed:

- sdk_v2/cpp/src/catalog/*

- sdk_v2/cpp/src/download/download_manager.cc

- sdk_v2/cpp/src/ep_detection/ep_detector.cc

- sdk_v2/cpp/src/inferencing/session/session.cc

- sdk_v2/cpp/src/manager.cc

- sdk_v2/cpp/src/service/web_service.cc

- sdk_v2/cpp/src/telemetry/*

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 49 out of 49 changed files in this pull request and generated 11 comments.

Comment on lines +158 to +161
ActionTracker create_tracker(Action::kSessionCreate, ctx_.telemetry, session_ctx);
create_tracker.SetModelId(model_name);
ChatSession session(*model, *loaded, ctx_.logger, ctx_.telemetry);
create_tracker.SetStatus(ActionStatus::kSuccess);
Comment on lines +206 to +209
ActionTracker create_tracker(Action::kSessionCreate, ctx_.telemetry, session_ctx);
create_tracker.SetModelId(model_name);
session = std::make_unique<ChatSession>(*model, *loaded, ctx_.logger, ctx_.telemetry);
create_tracker.SetStatus(ActionStatus::kSuccess);
Comment on lines +84 to +87
ActionTracker create_tracker(Action::kSessionCreate, ctx_.telemetry, session_ctx);
create_tracker.SetModelId(model_name);
EmbeddingsSession session(*model, *loaded, ctx_.logger, ctx_.telemetry);
create_tracker.SetStatus(ActionStatus::kSuccess);
Comment on lines +142 to +145
ActionTracker create_tracker(Action::kSessionCreate, ctx_.telemetry, session_ctx);
create_tracker.SetModelId(model_name);
AudioSession session(*model, *loaded, ctx_.logger, ctx_.telemetry);
create_tracker.SetStatus(ActionStatus::kSuccess);
Comment on lines +105 to +109
// Use the context the caller staged (an HTTP route stages an indirect child
// with the route's correlation id); otherwise mint a direct context per call
// for direct SDK use.
InvocationContext context = request_context_ ? *request_context_ : InvocationContext::Direct();
context.EnsureCorrelationId();
Comment thread sdk_v2/cpp/src/telemetry/telemetry.h Outdated
Comment on lines +140 to +142
/// Payload for the CatalogFetch event — emitted once per access to a model
/// catalog source (the live Azure catalog or the embedded static snapshot).
struct CatalogFetchInfo {
Comment on lines +256 to +260
// RAII telemetry tracker — emits a "Download" event on destruction with whatever
// fields have been populated. Default status is kFailure so abrupt exits (exceptions)
// are recorded as failures; the happy path explicitly sets kSuccess / kSkipped.
std::unique_ptr<DownloadTracker> tracker;
if (telemetry_ != nullptr) {
Comment on lines +84 to +88
bool TelemetryEnvironment::IsTruthyValue(std::string_view value) {
auto trimmed = Trim(value);
if (trimmed.empty()) {
return false;
}
Comment on lines +65 to +67
// Use the W variant so we don't depend on the legacy CRT _CRT_SECURE_NO_WARNINGS.
// Env-var values are ASCII for the CI flags we care about; if a value is unicode
// we still get the bytes round-tripped correctly because we only do truthiness checks.
Comment thread sdk_v2/cpp/src/download/download_manager.cc Outdated
bmehta001 added a commit that referenced this pull request Jul 13, 2026
Bring PR #879's second Copilot telemetry fixes into the hardening stack while retaining stronger redaction and lifecycle behavior already present here.

Files changed:

- sdk_v2/cpp/src/catalog/*

- sdk_v2/cpp/src/download/download_manager.cc

- sdk_v2/cpp/src/ep_detection/ep_detector.cc

- sdk_v2/cpp/src/inferencing/session/session.cc

- sdk_v2/cpp/src/service/web_service.cc

- sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
Scope session-create timing to construction, consume staged request telemetry context once, and clean up remaining telemetry contract/duplicate-download-wait details.

Files changed:

- sdk_v2/cpp/src/inferencing/session/*

- sdk_v2/cpp/src/service/*handler.cc

- sdk_v2/cpp/src/download/download_manager.cc

- sdk_v2/cpp/src/telemetry/telemetry.h

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
@bmehta001 bmehta001 requested a review from Copilot July 13, 2026 23:00
Copy ORT's 1DS offline-cache and teardown pattern: keep non-blocking teardown timeout, set an explicit cache DB path under the device-id cache directory, and call Flush before FlushAndTeardown.

Capture inference end time before telemetry-only message counting so Model TotalTimeMs excludes telemetry JSON parsing overhead.

Files changed:

- sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc

- sdk_v2/cpp/src/telemetry/device_id.{h,cc}

- sdk_v2/cpp/src/inferencing/session/session.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 61 out of 61 changed files in this pull request and generated 5 comments.

auto& config = LogManager::GetLogConfiguration();
config[CFG_BOOL_SESSION_RESET_ENABLED] = true;
config[CFG_INT_TRACE_LEVEL_MASK] = 0;
config[CFG_INT_MAX_TEARDOWN_TIME] = 0;
Comment thread sdk_v2/cpp/vcpkg.json Outdated
"telemetry": {
"description": "Build with 1DS (cpp-client-telemetry) telemetry uploads",
"dependencies": [
"cpp-client-telemetry",
Comment on lines +232 to +233
device_id_ = MakeGuidV4Hex();
if (WriteDeviceIdFile(file_path, device_id_)) {
Comment on lines +192 to +195
const bool regenerated_from_corruption = status_ == TelemetryDeviceIdStatus::kCorrupted;
device_id_ = MakeGuidV4Hex();
if (WriteWindowsRegistryDeviceId(device_id_)) {
status_ = regenerated_from_corruption ? TelemetryDeviceIdStatus::kCorrupted : TelemetryDeviceIdStatus::kNew;
Comment thread sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc Outdated
Add Privacy.md covering CI suppression, device identity storage, redaction, ProcessInfo, and 1DS cache behavior.

Guard the telemetry vcpkg feature and CMake option for UWP, and publish OneDsTelemetry initialized_ only after startup logging completes.

Files changed:

- sdk_v2/cpp/docs/Privacy.md

- sdk_v2/cpp/CMakeLists.txt

- sdk_v2/cpp/vcpkg.json

- sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 62 out of 62 changed files in this pull request and generated 5 comments.

auto& config = LogManager::GetLogConfiguration();
config[CFG_BOOL_SESSION_RESET_ENABLED] = true;
config[CFG_INT_TRACE_LEVEL_MASK] = 0;
config[CFG_INT_MAX_TEARDOWN_TIME] = 0;
Comment thread sdk_v2/cpp/CMakeLists.txt
# 1DS ingestion token. Treat as a secret: CI supplies it via the protected
# FOUNDRY_LOCAL_TELEMETRY_TOKEN environment variable so it is not exposed in the
# CMake command line or build.py logs. Empty dev/fork builds don't transmit.
set(FOUNDRY_LOCAL_TELEMETRY_TOKEN "$ENV{FOUNDRY_LOCAL_TELEMETRY_TOKEN}")
Comment on lines +168 to +172
void TelemetryDeviceId::InitializeLocked() {
if (initialized_) {
return;
}
initialized_ = true;
Comment on lines +418 to +421
} catch (const std::exception& e) {
if (tracker != nullptr) {
tracker->SetDownloadWaitResult("Failed");
tracker->RecordException(e);
Comment on lines +267 to +269
tracker->RecordDownloadComplete(ActionStatus::kSkipped,
was_registered_before ? "Registered" : "Unknown");
tracker->RecordRegisterComplete(ActionStatus::kSuccess, "Registered");
Add an ORT-style privacy notice near the top-level Foundry Local description and link to the SDK v2 C++ Privacy.md telemetry details.

Files changed:

- README.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 63 out of 63 changed files in this pull request and generated 5 comments.

auto& config = LogManager::GetLogConfiguration();
config[CFG_BOOL_SESSION_RESET_ENABLED] = true;
config[CFG_INT_TRACE_LEVEL_MASK] = 0;
config[CFG_INT_MAX_TEARDOWN_TIME] = 0;
Comment thread sdk_v2/cpp/CMakeLists.txt
Comment on lines +54 to +57
# 1DS ingestion token. Treat as a secret: CI supplies it via the protected
# FOUNDRY_LOCAL_TELEMETRY_TOKEN environment variable so it is not exposed in the
# CMake command line or build.py logs. Empty dev/fork builds don't transmit.
set(FOUNDRY_LOCAL_TELEMETRY_TOKEN "$ENV{FOUNDRY_LOCAL_TELEMETRY_TOKEN}")
Comment on lines +123 to +128
if (auto* semantic_context = LogManager::GetSemanticContext(); semantic_context != nullptr) {
const auto hashed_device_id = TelemetryDeviceId::HashForTelemetry(TelemetryDeviceId::Instance().GetValue());
if (!hashed_device_id.empty()) {
semantic_context->SetDeviceId(hashed_device_id);
}
}
Comment on lines +192 to +195
const bool regenerated_from_corruption = status_ == TelemetryDeviceIdStatus::kCorrupted;
device_id_ = MakeGuidV4Hex();
if (WriteWindowsRegistryDeviceId(device_id_)) {
status_ = regenerated_from_corruption ? TelemetryDeviceIdStatus::kCorrupted : TelemetryDeviceIdStatus::kNew;
Comment on lines +232 to +234
device_id_ = MakeGuidV4Hex();
if (WriteDeviceIdFile(file_path, device_id_)) {
status_ = regenerated_from_corruption ? TelemetryDeviceIdStatus::kCorrupted : TelemetryDeviceIdStatus::kNew;
FOUNDRY_TESTING_MODE stamped a `test` property on every event via an env var that
no longer serves a purpose. Remove it end to end -- IsTestingMode(), the test_mode
metadata field, the MakeEvent test_mode parameter and per-event `test` property,
the init log field, and the unit-test assertion -- so events carry no test flag.

Privacy.md now presents the build-time FOUNDRY_LOCAL_USE_TELEMETRY / --no_telemetry
option as the way to disable telemetry and drops the FOUNDRY_TESTING_MODE note.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bmehta001 added a commit that referenced this pull request Jul 14, 2026
Bring all remaining #879 telemetry updates into #880, including FOUNDRY_TESTING_MODE removal, privacy-doc updates, and the OneDsTelemetry merge resolution.

Files changed:

- sdk_v2/cpp/docs/Privacy.md

- sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc

- sdk_v2/cpp/src/telemetry/telemetry_environment.{h,cc}

- sdk_v2/cpp/src/telemetry/telemetry_logger.h

- sdk_v2/cpp/src/telemetry/telemetry_metadata.{h,cc}

- sdk_v2/cpp/test/internal_api/telemetry_test.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
Rewrite the SDK privacy doc as a high-level, user-facing statement matching ONNX Runtime and ONNX Runtime GenAI: standard Data Collection notice, an overview, what is collected, and how to disable. Drops implementation details (CI-upload suppression, exact device-id storage paths, offline-cache and inference-timing internals, tenant-token plumbing, UWP specifics).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add an opt-out telemetry control to the SDK configuration: set disable_telemetry to true (default false) and the Manager keeps only local diagnostic logging -- no 1DS uploader is created, no device identifier is written, and no ProcessInfo is emitted. Wired through the core fl::Configuration struct, the C API (flConfigurationApi::SetDisableTelemetry), and the header-only C++ wrapper (Configuration::SetDisableTelemetry). Privacy.md documents this as the way to disable telemetry; a config test covers the default and the field.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirror the C++ SDK's disable_telemetry option in the three language bindings so their users can opt out without rebuilding: C# Configuration.DisableTelemetry, Python Configuration(disable_telemetry=...), and JS/TS FoundryLocalConfig.disableTelemetry. Each mirrors the existing setter pattern; the C# delegate table and the CFFI cdef append SetDisableTelemetry last, matching the C flConfigurationApi order.

Validated: JS TypeScript typechecks and the native addon compiles; Python files syntax-check; the C++ core builds and its config tests pass. The C# build and Python editable install were blocked by this environment's restricted NuGet/pip feeds, not by the changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exercises the disable_telemetry option end to end through the CFFI cdef and the C API SetDisableTelemetry setter (Configuration._build_native applies it), mirroring the existing additional_settings/catalog_urls native-build cases.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DisableTelemetryDefaultsFalseAndIsSettable only asserted that a bool field stores a bool, and its default check duplicates the one now in DefaultValues. Keep the consolidated DefaultValues check, matching how every other config field's default is tested.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the disable_telemetry gating out of the Manager (which swapped TelemetryLogger vs OneDsTelemetry and was not unit-testable) into OneDsTelemetry itself, alongside its existing CI and empty-token suppression: when disabled the constructor skips 1DS Initialize, so IsUploadEnabled() is false and only the local diagnostic mirror runs. The Manager now always constructs OneDsTelemetry (when 1DS is built), passing config_.disable_telemetry.

This makes the behavior testable: OneDsTelemetryTest.DisableTelemetrySuppressesUpload constructs OneDsTelemetry with a non-empty token but disable_telemetry=true and asserts IsUploadEnabled() is false -- a real behavior check. Verified: objects + tests build and the test passes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants