Skip to content

[DRAFT] Add SDK v2 telemetry core#879

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

[DRAFT] Add SDK v2 telemetry core#879
bmehta001 wants to merge 49 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 22, 2026 8:09pm

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
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 Outdated
Comment thread sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc Outdated
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
Emit a dedicated AudioModel event after successful audio inference so audio models have modality-specific stats in addition to the generic Model event. The payload avoids file names and paths while capturing safe source kind, token counts, timing, stream/direct context, language hint, and PCM format/duration when available.

Files changed: sdk_v2/cpp/src/inferencing/generative/audio/audio_session.cc, sdk_v2/cpp/src/inferencing/generative/audio/audio_session.h, sdk_v2/cpp/src/inferencing/session/session.cc, sdk_v2/cpp/src/inferencing/session/session.h, sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc, sdk_v2/cpp/src/telemetry/one_ds_telemetry.h, sdk_v2/cpp/src/telemetry/telemetry.h, sdk_v2/cpp/src/telemetry/telemetry_logger.cc, sdk_v2/cpp/src/telemetry/telemetry_logger.h, 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
Port the ORT/OGA client-side sampling helper and stamp popSample on 1DS events while keeping the current sampling rate at 100%. This gives Foundry the same event-level sampling control point for future volume reduction without changing current collection behavior.

Files changed: sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc, sdk_v2/cpp/src/telemetry/telemetry_sampling.h, 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

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 83 out of 83 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc:180

  • A zero teardown timeout makes cpp-client-telemetry skip the upload-wait path and abort queued/pending requests during FlushAndTeardown(). Since the destructor relies on that call to deliver the final session and recently queued events, normal process shutdown can lose telemetry. Use a positive timeout (the upstream runtime default is 1 second; its functional tests commonly use 2 seconds).

Comment thread sdk_v2/cpp/src/manager.cc Outdated
Comment on lines +348 to +353
#if FOUNDRY_LOCAL_HAS_1DS
// OneDsTelemetry keeps an always-on local diagnostic mirror and uploads to 1DS only when telemetry is
// not disabled by configuration and not in a CI environment.
telemetry_ = std::make_unique<OneDsTelemetry>(config_.app_name, *logger_, config_.disable_telemetry);
#else
telemetry_ = std::make_unique<TelemetryLogger>(config_.app_name, *logger_);
Configure a positive 1DS teardown upload budget so FlushAndTeardown can upload queued events instead of aborting pending requests when maxTeardownUploadTimeInSec is zero.

Files changed: 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 83 out of 83 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

sdk_v2/cpp/src/manager.cc:354

  • disable_telemetry=true still installs a telemetry sink: OneDsTelemetry mirrors every event to TelemetryLogger, and the no-1DS branch always uses TelemetryLogger directly. Consequently model IDs, user agents, timings, and errors continue to be collected in local logs even though every public configuration contract says the SDK “collects and uploads nothing.” Select a no-op ITelemetry implementation when the option is set (before passing it to the detector, catalog, downloads, and sessions); keep local mirroring only for CI/transport-unavailable cases where telemetry was not explicitly disabled.
#if FOUNDRY_LOCAL_HAS_1DS
  // OneDsTelemetry keeps an always-on local diagnostic mirror and uploads to 1DS only when telemetry is
  // not disabled by configuration and not in a CI environment.
  telemetry_ = std::make_unique<OneDsTelemetry>(config_.app_name, *logger_, config_.disable_telemetry);
#else
  telemetry_ = std::make_unique<TelemetryLogger>(config_.app_name, *logger_);
#endif

Comment on lines +83 to +85
std::string out(static_cast<size_t>(needed - 1), '\0');
const int written = ::WideCharToMultiByte(CP_UTF8, 0, wide_value, -1, out.data(), needed, nullptr, nullptr);
return written > 0 ? TrimVersionString(std::move(out)) : std::string{};

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 83 out of 83 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

sdk_v2/cpp/src/manager.cc:353

  • disable_telemetry does not implement the documented “collects and uploads nothing” contract. OneDsTelemetry still sends every event to its TelemetryLogger mirror when disabled, while telemetry-off builds unconditionally select TelemetryLogger and ignore the flag entirely. Select a true no-op sink before wiring telemetry into the subsystems, and verify that disabling telemetry emits neither event payloads nor telemetry log records.
#if FOUNDRY_LOCAL_HAS_1DS
  // OneDsTelemetry keeps an always-on local diagnostic mirror and uploads to 1DS only when telemetry is
  // not disabled by configuration and not in a CI environment.
  telemetry_ = std::make_unique<OneDsTelemetry>(config_.app_name, *logger_, config_.disable_telemetry);
#else
  telemetry_ = std::make_unique<TelemetryLogger>(config_.app_name, *logger_);

Comment on lines +932 to +934
/// Optional. Disable all telemetry. When true, the SDK collects and uploads no telemetry (no 1DS
/// uploader is created and no device identifier is written). Defaults to false (telemetry enabled).
FL_API_STATUS(SetDisableTelemetry, _In_ flConfiguration* config, bool disable);
Comment on lines +62 to +64
void EpDownloadTracker::Done() {
RecordEvent(ActionStatus::kSkipped);
}
Comment on lines +17 to +19
- **Environment / device:** Foundry Local version, operating system, architecture, CPU details, and total memory, plus a device identifier. On Windows, Linux, and macOS the device identifier is a locally generated random UUID (**not** a hardware identifier such as a machine GUID); only a hashed form is transmitted, and it can be reset. On Android, Foundry Local uses the device identifier provided by the 1DS SDK instead of creating a separate Foundry-generated device id.
- **Usage:** model and execution-provider activity, model and execution-provider download activity, and request outcomes.
- **Errors:** error information for reliability analysis. Error text is scrubbed of filesystem paths, URLs, and other free-form content before transmission.
return;
}

const bool regenerated_from_corruption = saw_corruption_before_mutex || status_ == TelemetryDeviceIdStatus::kCorrupted;
Comment thread sdk_v2/cpp/CMakeLists.txt
Comment on lines 310 to 314
if(WIN32)
target_link_libraries(${TARGET} ${LINK_SCOPE} dbghelp bcrypt)
target_link_libraries(${TARGET} ${LINK_SCOPE} dbghelp bcrypt version)
# UWP builds have CMAKE_SYSTEM_NAME = "WindowsStore"; desktop = "Windows".
# WinHTTP is not available in UWP, so only define this on desktop Windows.
if(NOT CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
Honor disable_telemetry by installing a no-op ITelemetry sink, so events are neither uploaded nor mirrored to local telemetry logs when the public contract says telemetry is disabled.

Files changed: sdk_v2/cpp/src/manager.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 83 out of 83 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

sdk_v2/cpp/CMakeLists.txt:241

  • These telemetry sources are compiled for every WIN32 target even though only the 1DS dependency is disabled for WindowsStore. device_id.cc and telemetry_metadata.cc still compile desktop-only APIs such as RegCreateKeyExA, GetFileVersionInfoA, and GlobalMemoryStatusEx, so UWP builds can fail or fail app certification. Gate/provide UWP implementations for the core telemetry sources as well as disabling 1DS.
    src/service/audio_transcriptions_handler.cc
    src/service/embeddings_handler.cc
    src/service/chat_completions_handler.cc
    src/service/models_handlers.cc
    src/service/responses_handler.cc
    src/service/web_service.cc
    src/telemetry/telemetry.cc
    src/telemetry/telemetry_action_tracker.cc
    src/telemetry/device_id.cc

sdk_v2/cpp/src/ep_detection/ep_detector.cc:269

  • On every successful combined DownloadAndRegister call this reports the download phase as Skipped, even when the bootstrapper actually downloaded a package. That makes DownloadStatus/DownloadTimeMs inaccurate for the main success path. The bootstrapper result needs to expose whether download occurred (and its timing/status), or this event should report only an aggregate phase that can be measured correctly.
        tracker->RecordDownloadComplete(ActionStatus::kSkipped,
                                        was_registered_before ? "Registered" : "Unknown");
        tracker->RecordRegisterComplete(ActionStatus::kSuccess, "Registered");

CatalogFetchInfo base_info;
base_info.endpoint = parsed.endpoint;
base_info.region = parsed.region;
base_info.format = parsed.format;

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 83 out of 83 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (1)

sdk_v2/cpp/src/telemetry/telemetry_metadata.cc:85

  • needed includes the terminating NUL because the input length is -1, but out reserves only needed - 1 bytes while the conversion is allowed to write needed. On Windows this writes one byte past the string buffer whenever a host version is found. Allocate space for the terminator, then remove it after conversion.

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 83 out of 83 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (4)

sdk_v2/cpp/src/telemetry/telemetry_metadata.cc:84

  • needed includes the terminating NUL, but out reserves only needed - 1 bytes while WideCharToMultiByte is told it can write needed. On Windows this writes one byte past the string's size whenever an EXE version string is found. Allocate needed bytes; TrimVersionString will remove the trailing NUL.
    sdk_v2/cpp/docs/Privacy.md:17
  • The “What is collected” disclosure is incomplete relative to the upload implementation: ProcessInfo sends the process name and locale, common context sends the configured app name, action events send the HTTP User-Agent, and audio events can send the request language. Please disclose these fields (or stop uploading them) so this privacy document accurately describes the telemetry payload.
- **Environment / device:** Foundry Local version, operating system, architecture, CPU details, and total memory, plus a device identifier. On Windows, Linux, and macOS the device identifier is a locally generated random UUID (**not** a hardware identifier such as a machine GUID); only a hashed form is transmitted, and it can be reset. On Android, Foundry Local uses the device identifier provided by the 1DS SDK instead of creating a separate Foundry-generated device id.

sdk_v2/cpp/CMakeLists.txt:311

  • The newly compiled Windows device-ID implementation directly calls OpenProcessToken, GetTokenInformation, RegGetValueA, and RegCreateKeyExA, all of which require Advapi32.lib. It is compiled even when FOUNDRY_LOCAL_USE_TELEMETRY=OFF, so a no-telemetry Windows build cannot rely on the 1DS target to supply that library and will fail with unresolved externals.
        target_link_libraries(${TARGET} ${LINK_SCOPE} dbghelp bcrypt version)

sdk_v2/cpp/CMakeLists.txt:52

  • Turning off the 1DS transport is not sufficient to keep UWP buildable: device_id.cc and telemetry_metadata.cc are still compiled unconditionally and their _WIN32 branches use desktop-only registry/token/version APIs (RegGetValueA, OpenProcessToken, GetFileVersionInfoA). Add a WindowsStore-safe implementation or exclude those helpers and their Manager startup calls for UWP; otherwise the explicitly supported UWP configuration fails before the transport setting matters.
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
    set(FOUNDRY_LOCAL_USE_TELEMETRY OFF CACHE BOOL "1DS telemetry is not available for UWP" FORCE)
endif()

return HexEncode(digest, sizeof(digest));
}

std::string Sha256String(std::string_view value) {

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 83 out of 83 changed files in this pull request and generated 2 comments.

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

#include "http/HttpClient_Android.hpp"

## Disabling Telemetry

Telemetry is opt-out through the SDK configuration: set the `disable_telemetry` option to `true` before creating the manager, and the SDK collects and uploads nothing. In the C++ SDK, this is `Configuration::SetDisableTelemetry(true)`.

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 83 out of 83 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (3)

sdk_v2/cpp/src/telemetry/telemetry_metadata.cc:84

  • needed includes the terminating NUL, but out has only needed - 1 writable characters while the conversion is told it may write needed. This writes past the string's size on every successful Windows version lookup. Allocate needed, then remove the terminator after conversion.
    sdk_v2/cpp/include/foundry_local/foundry_local_c.h:934
  • Appending this function to the version-1 table without incrementing FOUNDRY_LOCAL_API_VERSION defeats the ABI version check. The updated C#/Python bindings still request v1, so when they load an older v1 native library they marshal/read this new slot past the old table and may call an arbitrary pointer instead of getting a clean version-mismatch failure. Introduce API v2 (and update each binding's requested version), while retaining the v1 table for older consumers.
  /// Optional. Disable all telemetry. When true, the SDK collects and uploads no telemetry (no 1DS
  /// uploader is created and no device identifier is written). Defaults to false (telemetry enabled).
  FL_API_STATUS(SetDisableTelemetry, _In_ flConfiguration* config, bool disable);

sdk_v2/cpp/src/util/sha256.cc:87

  • The only new assertion for this path checks the c: prefix and output length, so both platform implementations could return an incorrect 64-character digest undetected. Add Sha256String known-vector tests (including "abc" and the empty string), matching the existing Sha256File vectors in sha256_test.cc.

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 83 out of 83 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (4)

sdk_v2/cpp/CMakeLists.txt:311

  • The new Windows device-ID implementation calls OpenProcessToken, RegGetValueA, and RegCreateKeyExA, all of which require Advapi32.lib. This target does not link it, so Windows builds—especially the newly supported --no_telemetry configuration where MSTelemetry cannot supply a transitive dependency—fail with unresolved externals.
        target_link_libraries(${TARGET} ${LINK_SCOPE} dbghelp bcrypt version)

sdk_v2/cpp/CMakeLists.txt:52

  • Turning off 1DS is not sufficient to keep the WindowsStore build valid. device_id.cc and telemetry_metadata.cc are still compiled unconditionally and their _WIN32 branches call desktop-only registry and file-version APIs (RegGetValueA/RegCreateKeyExA and GetFileVersionInfoA). The UWP configuration therefore still compiles/links unsupported APIs; add WindowsStore-safe implementations or exclude these startup metadata paths for UWP.
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
    set(FOUNDRY_LOCAL_USE_TELEMETRY OFF CACHE BOOL "1DS telemetry is not available for UWP" FORCE)
endif()

sdk_v2/cpp/src/ep_detection/ep_detector.cc:269

  • RecordDownloadComplete is called only after the combined DownloadAndRegister operation has finished. Because that method measures elapsed time since RecordInitialState, the full operation is reported as a skipped download, while the immediately following successful register phase is reported as approximately 0 ms. Move the skipped download transition before invoking the combined bootstrapper so its elapsed time is attributed to registration.
        tracker->RecordDownloadComplete(ActionStatus::kSkipped,
                                        was_registered_before ? "Registered" : "Unknown");
        tracker->RecordRegisterComplete(ActionStatus::kSuccess, "Registered");

sdk_v2/cpp/src/ep_detection/ep_detector.cc:281

  • This failure path has the same phase-timing inversion: despite the comment saying the combined operation is recorded as registration, calling RecordDownloadComplete after it returns puts all elapsed time in download_duration_ms with status Skipped, then records a near-zero failed registration. Advance to the register stage before calling the bootstrapper.
        // Record the combined operation as the register phase rather than fabricating a download/register split.
        tracker->RecordDownloadComplete(ActionStatus::kSkipped,
                                        was_registered_before ? "Registered" : "Unknown");
        tracker->RecordRegisterComplete(ActionStatus::kFailure,
                                        was_registered_before ? "Registered" : "Unknown");

flStatusPtr (*AddWebServiceEndpoint)(flConfiguration* config, const char* url);
flStatusPtr (*SetExternalServiceUrl)(flConfiguration* config, const char* url);
flStatusPtr (*SetAdditionalOptions)(flConfiguration* config, const flKeyValuePairs* options);
flStatusPtr (*SetDisableTelemetry)(flConfiguration* config, bool disable);

Telemetry is limited to a small set of trace events emitted over the SDK's lifecycle:

- **Environment / device:** Foundry Local version, operating system, architecture, CPU details, and total memory, plus a device identifier. On Windows, Linux, and macOS the device identifier is a locally generated random UUID (**not** a hardware identifier such as a machine GUID); only a hashed form is transmitted, and it can be reset. On Android, Foundry Local uses the device identifier provided by the 1DS SDK instead of creating a separate Foundry-generated device id.
usage.correlation_id = context.correlation_id;
usage.indirect = context.indirect;
usage.stream = streaming;
usage.num_messages = CountRequestMessages(request);
Allocate space for the WideCharToMultiByte terminator when converting Windows version-resource strings, matching the ONNX Runtime fix and avoiding writing past the std::string size.

Files changed: 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
Port the ONNX Runtime GenAI telemetry lifetime decision from the global LOGMANAGER_INSTANCE singleton to a dedicated LogManagerProvider-owned instance. The 1DS configuration now outlives the provider, teardown releases the provider explicitly, and a shared lifecycle lock prevents concurrent event emission from racing destruction.

Files changed: sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc, sdk_v2/cpp/src/telemetry/one_ds_telemetry.h

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

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
Set ORT_TELEMETRY_DISABLED=1 at Azure and GitHub CI entry points so every CI job uses the same runtime telemetry opt-out variable across Foundry, ORT, and GenAI components.

Files changed: .pipelines/foundry-local-packaging.yml, .github/workflows/samples-integration-test.yml

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

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
When Configuration.disable_telemetry is true, call OrtApi::DisableTelemetryEvents on the shared OrtEnv so ORT telemetry is suppressed without mutating process-wide environment variables.

Leave the exact future GenAI hook, OgaSetTelemetryEnabled(false), as a TODO because the currently pinned GenAI headers do not expose it yet.

Files changed: sdk_v2/cpp/src/manager.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 85 out of 85 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (2)

sdk_v2/python/src/foundry_local_sdk/_native/build_cffi.py:477

  • cffi’s cdef parser does not accept the C bool spelling (the file’s preprocessing contract at line 46 explicitly requires _Bool). This new vtable member therefore prevents _cffi_bindings from being generated; use _Bool to match the other boolean ABI declarations.
    sdk_v2/cpp/src/telemetry/one_ds_telemetry.cc:218
  • This flag is set too late: if GetLogger returns null or throws, the successfully created provider is never torn down/released (the null path drops an Impl containing only a raw manager pointer, and the catch sees this flag as false). Mark initialization immediately after CreateLogManager succeeds and release the provider on the null-logger path.

// 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;
Resolve and call the exact OGA PR hook, OgaSetTelemetryEnabled(false), when Configuration.disable_telemetry is true. The lookup is dynamic so the current pinned GenAI headers can still build, and newer GenAI runtimes will be disabled without mutating process environment variables.

Files changed: sdk_v2/cpp/src/manager.cc

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

Copilot-Session: 297bc539-5f7f-4a70-9411-48e91a5bf532
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