OpenTelemetry Integration & Telemetry Control Flag (#762)
This commit is contained in:
parent
d3e43437a0
commit
2ebf2fbc82
|
@ -0,0 +1,285 @@
|
|||
# Gemini CLI Observability Guide
|
||||
|
||||
Telemetry provides crucial data about the Gemini CLI's performance, health, and usage. By enabling it, you can monitor operations, debug issues, and optimize tool usage through traces, metrics, and structured logs.
|
||||
|
||||
This entire system is built on the **[OpenTelemetry] (OTEL)** standard, allowing you to send data to any compatible backend, from your local terminal to a cloud service.
|
||||
|
||||
[OpenTelemetry]: https://opentelemetry.io/
|
||||
|
||||
## Quick Start: Enabling Telemetry
|
||||
|
||||
You can enable telemetry in multiple ways. [Configuration](configuration.md) is primarily managed via the `.gemini/settings.json` file and environment variables, but CLI flags can override these settings for a specific session.
|
||||
|
||||
**Order of Precedence:**
|
||||
|
||||
1. **CLI Flag (`--telemetry`):** These override all other settings for the current session.
|
||||
2. **Workspace Settings File (`.gemini/settings.json`):** If no CLI flag is used, the `telemetry` value from this project-specific file is used.
|
||||
3. **User Settings File (`~/.gemini/settings.json`):** If not set by a flag or workspace settings, the value from this global user file is used.
|
||||
4. **Default:** If telemetry is not configured by a flag or in any settings file, it is disabled.
|
||||
|
||||
Add this line to enable telemetry by in workspace (`.gemini/settings.json`) or user (`~/.gemini/settings.json`) settings:
|
||||
|
||||
```json
|
||||
{
|
||||
"telemetry": true
|
||||
}
|
||||
```
|
||||
|
||||
#### Mode 1: Console Output (Default)
|
||||
|
||||
If you only set `"telemetry": true` and do nothing else, the CLI will output all telemetry data directly to your console. This is the simplest way to inspect events, metrics, and traces without any external tools.
|
||||
|
||||
#### Mode 2: Sending to a Collector
|
||||
|
||||
To send data to a local or remote OpenTelemetry collector, set the following environment variable:
|
||||
|
||||
```bash
|
||||
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
|
||||
```
|
||||
|
||||
The CLI sends data using the OTLP/gRPC protocol.
|
||||
|
||||
Learn more about OTEL exporter standard configuration in [documentation][otel-config-docs].
|
||||
|
||||
[otel-config-docs]: https://opentelemetry.io/docs/languages/sdk-configuration/otlp-exporter/
|
||||
|
||||
## Running an OTEL Collector
|
||||
|
||||
An OTEL Collector is a service that receives, processes, and exports telemetry data. Below are common setups.
|
||||
|
||||
### Local
|
||||
|
||||
This setup prints all telemetry from the Gemini CLI to your terminal using a local Docker container.
|
||||
|
||||
**1. Create a Configuration File**
|
||||
|
||||
Create the file `.gemini/otel/collector-local.yaml` with the following:
|
||||
|
||||
```bash
|
||||
cat <<EOF > .gemini/otel/collector-local.yaml
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: "0.0.0.0:4317"
|
||||
|
||||
processors:
|
||||
batch:
|
||||
timeout: 1s
|
||||
|
||||
exporters:
|
||||
debug:
|
||||
verbosity: detailed
|
||||
|
||||
service:
|
||||
telemetry:
|
||||
logs:
|
||||
level: "debug"
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
exporters: [debug]
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
exporters: [debug]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
exporters: [debug]
|
||||
EOF
|
||||
```
|
||||
|
||||
**2. Run the Collector**
|
||||
|
||||
In your terminal, run this Docker command:
|
||||
|
||||
```bash
|
||||
docker run --rm --name otel-collector-local \
|
||||
-p 4317:4317 \
|
||||
-v "$(pwd)/.gemini/otel/collector-local.yaml":/etc/otelcol-contrib/config.yaml \
|
||||
otel/opentelemetry-collector-contrib:latest
|
||||
```
|
||||
|
||||
**3. Stop the Collector**
|
||||
|
||||
```bash
|
||||
docker stop otel-collector-local
|
||||
```
|
||||
|
||||
### Google Cloud
|
||||
|
||||
This setup sends all telemetry to Google Cloud for robust, long-term analysis.
|
||||
|
||||
**1. Prerequisites**
|
||||
|
||||
- A Google Cloud Project ID.
|
||||
- **APIs Enabled**: Cloud Trace, Cloud Monitoring, Cloud Logging.
|
||||
- **Authentication**: A Service Account with the roles `Cloud Trace Agent`, `Monitoring Metric Writer`, and `Logs Writer`. Ensure your environment is authenticated (e.g., via `gcloud auth application-default login` or a service account key file).
|
||||
|
||||
**2. Create a Configuration File**
|
||||
|
||||
Create `.gemini/otel/collector-gcp.yaml`:
|
||||
|
||||
```bash
|
||||
cat <<EOF > .gemini/otel/collector-gcp.yaml
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: "0.0.0.0:4317"
|
||||
|
||||
processors:
|
||||
batch:
|
||||
timeout: 1s
|
||||
|
||||
exporters:
|
||||
googlecloud:
|
||||
project: "${GOOGLE_CLOUD_PROJECT}"
|
||||
trace:
|
||||
metric:
|
||||
prefix: "custom.googleapis.com/gemini_code"
|
||||
log:
|
||||
default_log_name: "gemini_code"
|
||||
debug:
|
||||
verbosity: detailed
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
exporters: [googlecloud]
|
||||
metrics:
|
||||
receivers: [otlp]
|
||||
exporters: [googlecloud]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
exporters: [googlecloud]
|
||||
EOF
|
||||
```
|
||||
|
||||
**3. Run the Collector**
|
||||
|
||||
This command mounts your Google Cloud credentials into the container.
|
||||
|
||||
If using application default credentials:
|
||||
|
||||
```bash
|
||||
docker run --rm --name otel-collector-gcp \
|
||||
-p 4317:4317 \
|
||||
-v "/home/user/.config/gcloud/application_default_credentials.json":/etc/gcp/credentials.json \
|
||||
-e "GOOGLE_APPLICATION_CREDENTIALS=/etc/gcp/credentials.json" \
|
||||
-v "$(pwd)/.gemini/otel/collector-gcp.yaml":/etc/otelcol-contrib/config.yaml \
|
||||
otel/opentelemetry-collector-contrib:latest --config /etc/otelcol-contrib/config.yaml
|
||||
```
|
||||
|
||||
If using sevice account key:
|
||||
|
||||
```bash
|
||||
docker run --rm --name otel-collector-gcp \
|
||||
-p 4317:4317 \
|
||||
-v "/path/to/your/sa-key.json":/etc/gcp/sa-key.json:ro \
|
||||
-e "GOOGLE_APPLICATION_CREDENTIALS=/etc/gcp/sa-key.json" \
|
||||
-v "$(pwd)/.gemini/otel/collector-gcp.yaml":/etc/otelcol-contrib/config.yaml \
|
||||
otel/opentelemetry-collector-contrib:latest --config /etc/otelcol-contrib/config.yaml
|
||||
```
|
||||
|
||||
Your telemetry data will now appear in Cloud Trace, Monitoring, and Logging.
|
||||
|
||||
**3. Stop the Collector**
|
||||
|
||||
```bash
|
||||
docker stop otel-collector-gcp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Reference: Logs & Metrics
|
||||
|
||||
### Logs
|
||||
|
||||
These are timestamped records of specific events.
|
||||
|
||||
- `gemini_code.config`: Fired once at startup with the CLI's configuration.
|
||||
|
||||
- **Attributes**:
|
||||
- `model` (string)
|
||||
- `sandbox_enabled` (boolean)
|
||||
- `core_tools_enabled` (string)
|
||||
- `approval_mode` (string)
|
||||
- `vertex_ai_enabled` (boolean)
|
||||
- `log_user_prompts_enabled` (boolean)
|
||||
- `file_filtering_respect_git_ignore` (boolean)
|
||||
- `file_filtering_allow_build_artifacts` (boolean)
|
||||
|
||||
- `gemini_code.user_prompt`: Fired when a user submits a prompt.
|
||||
|
||||
- **Attributes**:
|
||||
- `prompt_char_count`
|
||||
- `prompt` (except if `log_user_prompts_enabled` is false)
|
||||
|
||||
- `gemini_code.tool_call`: Fired for every function call.
|
||||
|
||||
- **Attributes**:
|
||||
- `function_name`
|
||||
- `function_args`
|
||||
- `duration_ms`
|
||||
- `success` (boolean)
|
||||
- `error` (optional)
|
||||
- `error_type` (optional)
|
||||
|
||||
- `gemini_code.api_request`: Fired when making a request to the Gemini API.
|
||||
|
||||
- **Attributes**:
|
||||
- `model`
|
||||
- `duration_ms`
|
||||
- `prompt_token_count`
|
||||
|
||||
- `gemini_code.api_error`: Fired if the API request fails.
|
||||
|
||||
- **Attributes**:
|
||||
- `model`
|
||||
- `error`
|
||||
- `error_type`
|
||||
- `status_code`
|
||||
- `duration_ms`
|
||||
- `attempt`
|
||||
|
||||
- `gemini_code.api_response`: Fired upon receiving a response from the Gemini API.
|
||||
- **Attributes**:
|
||||
- `model`
|
||||
- `status_code`
|
||||
- `duration_ms`
|
||||
- `error` (optional)
|
||||
- `attempt`
|
||||
|
||||
### Metrics
|
||||
|
||||
These are numerical measurements of behavior over time.
|
||||
|
||||
- `gemini_code.session.count` (Counter, Int): Incremented once per CLI startup.
|
||||
|
||||
- `gemini_code.tool.call.count` (Counter, Int): Counts tool calls.
|
||||
|
||||
- **Attributes**:
|
||||
- `function_name`
|
||||
- `success` (boolean)
|
||||
|
||||
- `gemini_code.tool.call.latency` (Histogram, ms): Measures tool call latency.
|
||||
|
||||
- **Attributes**:
|
||||
- `function_name`
|
||||
|
||||
- `gemini_code.api.request.count` (Counter, Int): Counts all API requests.
|
||||
|
||||
- **Attributes**:
|
||||
- `model`
|
||||
- `status_code`
|
||||
- `error_type` (optional)
|
||||
|
||||
- `gemini_code.api.request.latency` (Histogram, ms): Measures API request latency.
|
||||
|
||||
- **Attributes**:
|
||||
- `model`
|
||||
|
||||
- `gemini_code.token.input.count` (Counter, Int): Counts the total number of input tokens sent to the API.
|
||||
- **Attributes**:
|
||||
- `model`
|
|
@ -918,6 +918,37 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@grpc/grpc-js": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.13.4.tgz",
|
||||
"integrity": "sha512-GsFaMXCkMqkKIvwCQjCrwH+GHbPKBjhwo/8ZuUkWHqbI73Kky9I+pQltrlT0+MWpedCoosda53lgjYfyEPgxBg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/proto-loader": "^0.7.13",
|
||||
"@js-sdsl/ordered-map": "^4.4.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@grpc/proto-loader": {
|
||||
"version": "0.7.15",
|
||||
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz",
|
||||
"integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"lodash.camelcase": "^4.3.0",
|
||||
"long": "^5.0.0",
|
||||
"protobufjs": "^7.2.5",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
"proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
|
||||
|
@ -1065,6 +1096,16 @@
|
|||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@js-sdsl/ordered-map": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz",
|
||||
"integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/js-sdsl"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsonjoy.com/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz",
|
||||
|
@ -1179,6 +1220,472 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
|
||||
"integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/api-logs": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.52.1.tgz",
|
||||
"integrity": "sha512-qnSqB2DQ9TPP96dl8cDubDvrUyWc0/sK81xHTK8eSUspzDM3bsewX903qclQFvVhgStjRWdC5bLb3kQqMkfV5A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/context-async-hooks": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.25.1.tgz",
|
||||
"integrity": "sha512-UW/ge9zjvAEmRWVapOP0qyCvPulWU6cQxGxDbWEFfGOj1VBBZAuOqTo3X6yWmDTD3Xe15ysCZChHncr2xFMIfQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/core": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.25.1.tgz",
|
||||
"integrity": "sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-logs-otlp-grpc": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-grpc/-/exporter-logs-otlp-grpc-0.52.1.tgz",
|
||||
"integrity": "sha512-sXgcp4fsL3zCo96A0LmFIGYOj2LSEDI6wD7nBYRhuDDxeRsk18NQgqRVlCf4VIyTBZzGu1M7yOtdFukQPgII1A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.7.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||
"@opentelemetry/sdk-logs": "0.52.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-metrics-otlp-grpc": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-grpc/-/exporter-metrics-otlp-grpc-0.52.1.tgz",
|
||||
"integrity": "sha512-CE0f1IEE1GQj8JWl/BxKvKwx9wBTLR09OpPQHaIs5LGBw3ODu8ek5kcbrHPNsFYh/pWh+pcjbZQoxq3CqvQVnA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.7.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/exporter-metrics-otlp-http": "0.52.1",
|
||||
"@opentelemetry/otlp-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-metrics": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-metrics-otlp-http": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-metrics-otlp-http/-/exporter-metrics-otlp-http-0.52.1.tgz",
|
||||
"integrity": "sha512-oAHPOy1sZi58bwqXaucd19F/v7+qE2EuVslQOEeLQT94CDuZJJ4tbWzx8DpYBTrOSzKqqrMtx9+PMxkrcbxOyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-metrics": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-trace-otlp-grpc": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-grpc/-/exporter-trace-otlp-grpc-0.52.1.tgz",
|
||||
"integrity": "sha512-pVkSH20crBwMTqB3nIN4jpQKUEoB0Z94drIHpYyEqs7UBr+I0cpYyOR3bqjA/UasQUMROb3GX8ZX4/9cVRqGBQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.7.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-grpc-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-trace-otlp-http": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.52.1.tgz",
|
||||
"integrity": "sha512-05HcNizx0BxcFKKnS5rwOV+2GevLTVIRA0tRgWYyw4yCgR53Ic/xk83toYKts7kbzcI+dswInUg/4s8oyA+tqg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-trace-otlp-proto": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-proto/-/exporter-trace-otlp-proto-0.52.1.tgz",
|
||||
"integrity": "sha512-pt6uX0noTQReHXNeEslQv7x311/F1gJzMnp1HD2qgypLRPbXDeMzzeTngRTUaUbP6hqWNtPxuLr4DEoZG+TcEQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/exporter-zipkin": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/exporter-zipkin/-/exporter-zipkin-1.25.1.tgz",
|
||||
"integrity": "sha512-RmOwSvkimg7ETwJbUOPTMhJm9A9bG1U8s7Zo3ajDh4zM7eYcycQ0dM7FbLD6NXWbI2yj7UY4q8BKinKYBQksyw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.52.1.tgz",
|
||||
"integrity": "sha512-uXJbYU/5/MBHjMp1FqrILLRuiJCs3Ofk0MeRDk8g1S1gD47U8X3JnSwcMO1rtRo1x1a7zKaQHaoYu49p/4eSKw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.52.1",
|
||||
"@types/shimmer": "^1.0.2",
|
||||
"import-in-the-middle": "^1.8.1",
|
||||
"require-in-the-middle": "^7.1.1",
|
||||
"semver": "^7.5.2",
|
||||
"shimmer": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-http": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation-http/-/instrumentation-http-0.52.1.tgz",
|
||||
"integrity": "sha512-dG/aevWhaP+7OLv4BQQSEKMJv8GyeOp3Wxl31NHqE8xo9/fYMfEljiZphUHIfyg4gnZ9swMyWjfOQs5GUQe54Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/instrumentation": "0.52.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1",
|
||||
"semver": "^7.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation-http/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/instrumentation/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/otlp-exporter-base": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.52.1.tgz",
|
||||
"integrity": "sha512-z175NXOtX5ihdlshtYBe5RpGeBoTXVCKPPLiQlD6FHvpM4Ch+p2B0yWKYSrBfLH24H9zjJiBdTrtD+hLlfnXEQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/otlp-grpc-exporter-base": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/otlp-grpc-exporter-base/-/otlp-grpc-exporter-base-0.52.1.tgz",
|
||||
"integrity": "sha512-zo/YrSDmKMjG+vPeA9aBBrsQM9Q/f2zo6N04WMB3yNldJRsgpRBeLLwvAt/Ba7dpehDLOEFBd1i2JCoaFtpCoQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@grpc/grpc-js": "^1.7.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/otlp-exporter-base": "0.52.1",
|
||||
"@opentelemetry/otlp-transformer": "0.52.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/otlp-transformer": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.52.1.tgz",
|
||||
"integrity": "sha512-I88uCZSZZtVa0XniRqQWKbjAUm73I8tpEy/uJYPPYw5d7BRdVk0RfTBQw8kSUl01oVWEuqxLDa802222MYyWHg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.52.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-logs": "0.52.1",
|
||||
"@opentelemetry/sdk-metrics": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1",
|
||||
"protobufjs": "^7.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.3.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/propagator-b3": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/propagator-b3/-/propagator-b3-1.25.1.tgz",
|
||||
"integrity": "sha512-p6HFscpjrv7//kE+7L+3Vn00VEDUJB0n6ZrjkTYHrJ58QZ8B3ajSJhRbCcY6guQ3PDjTbxWklyvIN2ojVbIb1A==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/propagator-jaeger": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/propagator-jaeger/-/propagator-jaeger-1.25.1.tgz",
|
||||
"integrity": "sha512-nBprRf0+jlgxks78G/xq72PipVK+4or9Ypntw0gVZYNTCSK8rg5SeaGV19tV920CMqBD/9UIOiFr23Li/Q8tiA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/resources": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.25.1.tgz",
|
||||
"integrity": "sha512-pkZT+iFYIZsVn6+GzM0kSX+u3MSLCY9md+lIJOoKl/P+gJFfxJte/60Usdp8Ce4rOs8GduUpSPNe1ddGyDT1sQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-logs": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.52.1.tgz",
|
||||
"integrity": "sha512-MBYh+WcPPsN8YpRHRmK1Hsca9pVlyyKd4BxOC4SsgHACnl/bPp4Cri9hWhVm5+2tiQ9Zf4qSc1Jshw9tOLGWQA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.52.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.4.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-metrics": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.25.1.tgz",
|
||||
"integrity": "sha512-9Mb7q5ioFL4E4dDrc4wC/A3NTHDat44v4I3p2pLPSxRvqUbDIQyMVr9uK+EU69+HWhlET1VaSrRzwdckWqY15Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"lodash.merge": "^4.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.3.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-node": {
|
||||
"version": "0.52.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-node/-/sdk-node-0.52.1.tgz",
|
||||
"integrity": "sha512-uEG+gtEr6eKd8CVWeKMhH2olcCHM9dEK68pe0qE0be32BcCRsvYURhHaD1Srngh1SQcnQzZ4TP324euxqtBOJA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/api-logs": "0.52.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "0.52.1",
|
||||
"@opentelemetry/exporter-trace-otlp-http": "0.52.1",
|
||||
"@opentelemetry/exporter-trace-otlp-proto": "0.52.1",
|
||||
"@opentelemetry/exporter-zipkin": "1.25.1",
|
||||
"@opentelemetry/instrumentation": "0.52.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/sdk-logs": "0.52.1",
|
||||
"@opentelemetry/sdk-metrics": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-node": "1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.3.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-trace-base": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.25.1.tgz",
|
||||
"integrity": "sha512-C8k4hnEbc5FamuZQ92nTOp8X/diCY56XUTnMiv9UTuJitCzaNNHAVsdm5+HLCdI8SLQsLWIrG38tddMxLVoftw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/resources": "1.25.1",
|
||||
"@opentelemetry/semantic-conventions": "1.25.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-trace-node": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-1.25.1.tgz",
|
||||
"integrity": "sha512-nMcjFIKxnFqoez4gUmihdBrbpsEnAX/Xj16sGvZm+guceYE0NE00vLhpDVK6f3q8Q4VFI5xG8JjlXKMB/SkTTQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@opentelemetry/context-async-hooks": "1.25.1",
|
||||
"@opentelemetry/core": "1.25.1",
|
||||
"@opentelemetry/propagator-b3": "1.25.1",
|
||||
"@opentelemetry/propagator-jaeger": "1.25.1",
|
||||
"@opentelemetry/sdk-trace-base": "1.25.1",
|
||||
"semver": "^7.5.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@opentelemetry/api": ">=1.0.0 <1.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/sdk-trace-node/node_modules/semver": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
|
||||
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@opentelemetry/semantic-conventions": {
|
||||
"version": "1.25.1",
|
||||
"resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.25.1.tgz",
|
||||
"integrity": "sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
|
@ -1190,6 +1697,70 @@
|
|||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||
"integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||
"integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1",
|
||||
"@protobufjs/inquire": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/pool": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.41.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.41.1.tgz",
|
||||
|
@ -1651,7 +2222,6 @@
|
|||
"version": "20.17.57",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.57.tgz",
|
||||
"integrity": "sha512-f3T4y6VU4fVQDKVqJV4Uppy8c1p/sVvS3peyqxyWnzkqXFJLRU7Y1Bl7rMS1Qe9z0v4M6McY0Fp9yBsgHJUsWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.19.2"
|
||||
|
@ -1698,6 +2268,12 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/shimmer": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.2.0.tgz",
|
||||
"integrity": "sha512-UE7oxhQLLd9gub6JKIAhDq06T0F6FnztwMNRvYgjeQSBeMc1ZG/tA47EwfduvkuQS8apbkM/lpLpWsaCeYsXVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/tinycolor2": {
|
||||
"version": "1.4.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz",
|
||||
|
@ -2183,7 +2759,6 @@
|
|||
"version": "8.14.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
|
||||
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
|
@ -2192,6 +2767,15 @@
|
|||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-import-attributes": {
|
||||
"version": "1.9.5",
|
||||
"resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz",
|
||||
"integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"acorn": "^8"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-jsx": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
|
@ -2753,6 +3337,12 @@
|
|||
"node": ">= 16"
|
||||
}
|
||||
},
|
||||
"node_modules/cjs-module-lexer": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz",
|
||||
"integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cli-boxes": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz",
|
||||
|
@ -4958,6 +5548,18 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/import-in-the-middle": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.14.0.tgz",
|
||||
"integrity": "sha512-g5zLT0HaztRJWysayWYiUq/7E5H825QIiecMD2pI5QO7Wzr847l6GDvPvmZaDIdrDtS2w7qRczywxiK6SL5vRw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"acorn": "^8.14.0",
|
||||
"acorn-import-attributes": "^1.9.5",
|
||||
"cjs-module-lexer": "^1.2.2",
|
||||
"module-details-from-path": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/imurmurhash": {
|
||||
"version": "0.1.4",
|
||||
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
|
||||
|
@ -5405,7 +6007,6 @@
|
|||
"version": "2.16.1",
|
||||
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
|
||||
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hasown": "^2.0.2"
|
||||
|
@ -6175,13 +6776,24 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.camelcase": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||
"integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.merge": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
|
@ -6424,6 +7036,12 @@
|
|||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/module-details-from-path": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.4.tgz",
|
||||
"integrity": "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
|
@ -6891,7 +7509,6 @@
|
|||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
|
||||
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
|
@ -7075,6 +7692,30 @@
|
|||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.5.3",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.3.tgz",
|
||||
"integrity": "sha512-sildjKwVqOI2kmFDiXQ6aEB0fjYTafpEvIBs8tOR8qI4spuL9OPROLVu2qZqi/xgCfsHIwVqlaF8JBjWFHnKbw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.4",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.0",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -7305,6 +7946,20 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/require-in-the-middle": {
|
||||
"version": "7.5.2",
|
||||
"resolved": "https://registry.npmjs.org/require-in-the-middle/-/require-in-the-middle-7.5.2.tgz",
|
||||
"integrity": "sha512-gAZ+kLqBdHarXB64XpAe2VCjB7rIRv+mU8tfRWziHRJ5umKsIHN2tLLv6EtMw7WCdP19S0ERVMldNvxYCHnhSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.5",
|
||||
"module-details-from-path": "^1.0.3",
|
||||
"resolve": "^1.22.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requireindex": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz",
|
||||
|
@ -7319,7 +7974,6 @@
|
|||
"version": "1.22.10",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
|
||||
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.16.0",
|
||||
|
@ -7735,6 +8389,12 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/shimmer": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz",
|
||||
"integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
|
@ -8211,7 +8871,6 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
|
||||
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
@ -8713,7 +9372,6 @@
|
|||
"version": "6.19.8",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
|
||||
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unicorn-magic": {
|
||||
|
@ -9575,6 +10233,12 @@
|
|||
"dependencies": {
|
||||
"@google/genai": "^1.0.1",
|
||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-http": "^0.52.0",
|
||||
"@opentelemetry/sdk-node": "^0.52.0",
|
||||
"diff": "^7.0.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"fast-glob": "^3.3.3",
|
||||
|
|
|
@ -53,6 +53,7 @@ vi.mock('@gemini-code/core', async () => {
|
|||
getGeminiMdFileCount: () => params.geminiMdFileCount,
|
||||
getVertexAI: () => params.vertexai,
|
||||
getShowMemoryUsage: () => params.showMemoryUsage, // Added for the test
|
||||
getTelemetry: () => params.telemetry,
|
||||
// Add any other methods that are called on the config object
|
||||
setUserMemory: vi.fn(),
|
||||
setGeminiMdFileCount: vi.fn(),
|
||||
|
@ -108,6 +109,72 @@ describe('loadCliConfig', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('loadCliConfig telemetry', () => {
|
||||
const originalArgv = process.argv;
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
vi.mocked(os.homedir).mockReturnValue(MOCK_HOME_DIR);
|
||||
process.env.GEMINI_API_KEY = 'test-api-key';
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.argv = originalArgv;
|
||||
process.env = originalEnv;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
it('should set telemetry to false by default when no flag or setting is present', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const settings: Settings = {};
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(false);
|
||||
});
|
||||
|
||||
it('should set telemetry to true when --telemetry flag is present', async () => {
|
||||
process.argv = ['node', 'script.js', '--telemetry'];
|
||||
const settings: Settings = {};
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(true);
|
||||
});
|
||||
|
||||
it('should set telemetry to false when --no-telemetry flag is present', async () => {
|
||||
process.argv = ['node', 'script.js', '--no-telemetry'];
|
||||
const settings: Settings = {};
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(false);
|
||||
});
|
||||
|
||||
it('should use telemetry value from settings if CLI flag is not present (settings true)', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const settings: Settings = { telemetry: true };
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(true);
|
||||
});
|
||||
|
||||
it('should use telemetry value from settings if CLI flag is not present (settings false)', async () => {
|
||||
process.argv = ['node', 'script.js'];
|
||||
const settings: Settings = { telemetry: false };
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(false);
|
||||
});
|
||||
|
||||
it('should prioritize --telemetry CLI flag (true) over settings (false)', async () => {
|
||||
process.argv = ['node', 'script.js', '--telemetry'];
|
||||
const settings: Settings = { telemetry: false };
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(true);
|
||||
});
|
||||
|
||||
it('should prioritize --no-telemetry CLI flag (false) over settings (true)', async () => {
|
||||
process.argv = ['node', 'script.js', '--no-telemetry'];
|
||||
const settings: Settings = { telemetry: true };
|
||||
const result = await loadCliConfig(settings);
|
||||
expect(result.config.getTelemetry()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
|
|
|
@ -45,6 +45,7 @@ interface CliArgs {
|
|||
all_files: boolean | undefined;
|
||||
show_memory_usage: boolean | undefined;
|
||||
yolo: boolean | undefined;
|
||||
telemetry: boolean | undefined;
|
||||
}
|
||||
|
||||
async function parseArguments(): Promise<CliArgs> {
|
||||
|
@ -89,6 +90,10 @@ async function parseArguments(): Promise<CliArgs> {
|
|||
'Automatically accept all actions (aka YOLO mode, see https://www.youtube.com/watch?v=xvFZjo5PgG0 for more details)?',
|
||||
default: false,
|
||||
})
|
||||
.option('telemetry', {
|
||||
type: 'boolean',
|
||||
description: 'Enable telemetry?',
|
||||
})
|
||||
.version() // This will enable the --version flag based on package.json
|
||||
.help()
|
||||
.alias('h', 'help')
|
||||
|
@ -214,6 +219,10 @@ export async function loadCliConfig(
|
|||
argv.show_memory_usage || settings.showMemoryUsage || false,
|
||||
geminiIgnorePatterns,
|
||||
accessibility: settings.accessibility,
|
||||
telemetry:
|
||||
argv.telemetry !== undefined
|
||||
? argv.telemetry
|
||||
: (settings.telemetry ?? false),
|
||||
// Git-aware file filtering settings
|
||||
fileFilteringRespectGitIgnore: settings.fileFiltering?.respectGitIgnore,
|
||||
fileFilteringAllowBuildArtifacts:
|
||||
|
|
|
@ -243,6 +243,62 @@ describe('Settings Loading and Merging', () => {
|
|||
expect(settings.merged.contextFileName).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should load telemetry setting from user settings', () => {
|
||||
(mockFsExistsSync as Mock).mockImplementation(
|
||||
(p: fs.PathLike) => p === USER_SETTINGS_PATH,
|
||||
);
|
||||
const userSettingsContent = { telemetry: true };
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
expect(settings.merged.telemetry).toBe(true);
|
||||
});
|
||||
|
||||
it('should load telemetry setting from workspace settings', () => {
|
||||
(mockFsExistsSync as Mock).mockImplementation(
|
||||
(p: fs.PathLike) => p === MOCK_WORKSPACE_SETTINGS_PATH,
|
||||
);
|
||||
const workspaceSettingsContent = { telemetry: false };
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
expect(settings.merged.telemetry).toBe(false);
|
||||
});
|
||||
|
||||
it('should prioritize workspace telemetry setting over user setting', () => {
|
||||
(mockFsExistsSync as Mock).mockReturnValue(true);
|
||||
const userSettingsContent = { telemetry: true };
|
||||
const workspaceSettingsContent = { telemetry: false };
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
(p: fs.PathOrFileDescriptor) => {
|
||||
if (p === USER_SETTINGS_PATH)
|
||||
return JSON.stringify(userSettingsContent);
|
||||
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
|
||||
return JSON.stringify(workspaceSettingsContent);
|
||||
return '{}';
|
||||
},
|
||||
);
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
expect(settings.merged.telemetry).toBe(false);
|
||||
});
|
||||
|
||||
it('should have telemetry as undefined if not in any settings file', () => {
|
||||
(mockFsExistsSync as Mock).mockReturnValue(false); // No settings files exist
|
||||
(fs.readFileSync as Mock).mockReturnValue('{}');
|
||||
const settings = loadSettings(MOCK_WORKSPACE_DIR);
|
||||
expect(settings.merged.telemetry).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle JSON parsing errors gracefully', () => {
|
||||
(mockFsExistsSync as Mock).mockReturnValue(true);
|
||||
(fs.readFileSync as Mock).mockImplementation(
|
||||
|
|
|
@ -36,6 +36,7 @@ export interface Settings {
|
|||
showMemoryUsage?: boolean;
|
||||
contextFileName?: string;
|
||||
accessibility?: AccessibilitySettings;
|
||||
telemetry?: boolean;
|
||||
|
||||
// Git-aware file filtering settings
|
||||
fileFiltering?: {
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
Config,
|
||||
MessageSenderType,
|
||||
ToolCallRequestInfo,
|
||||
logUserPrompt,
|
||||
} from '@gemini-code/core';
|
||||
import { type PartListUnion } from '@google/genai';
|
||||
import {
|
||||
|
@ -178,6 +179,10 @@ export const useGeminiStream = (
|
|||
|
||||
if (typeof query === 'string') {
|
||||
const trimmedQuery = query.trim();
|
||||
logUserPrompt(config, {
|
||||
prompt: trimmedQuery,
|
||||
prompt_char_count: trimmedQuery.length,
|
||||
});
|
||||
onDebugMessage(`User query: '${trimmedQuery}'`);
|
||||
await logger?.logMessage(MessageSenderType.USER, trimmedQuery);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import {
|
|||
Tool,
|
||||
ToolCall,
|
||||
Status as CoreStatus,
|
||||
logToolCall,
|
||||
} from '@gemini-code/core';
|
||||
import { useCallback, useEffect, useState, useRef } from 'react';
|
||||
import {
|
||||
|
@ -108,6 +109,30 @@ export function useReactToolScheduler(
|
|||
const allToolCallsCompleteHandler: AllToolCallsCompleteHandler = (
|
||||
completedToolCalls,
|
||||
) => {
|
||||
completedToolCalls.forEach((call) => {
|
||||
let success = false;
|
||||
let errorMessage: string | undefined;
|
||||
let duration = 0;
|
||||
|
||||
if (call.status === 'success') {
|
||||
success = true;
|
||||
}
|
||||
if (
|
||||
call.status === 'error' &&
|
||||
typeof call.response.resultDisplay === 'string'
|
||||
) {
|
||||
errorMessage = call.response.resultDisplay;
|
||||
}
|
||||
duration = call.durationMs || 0;
|
||||
|
||||
logToolCall({
|
||||
function_name: call.request.name,
|
||||
function_args: call.request.args,
|
||||
duration_ms: duration,
|
||||
success,
|
||||
error: errorMessage,
|
||||
});
|
||||
});
|
||||
onComplete(completedToolCalls);
|
||||
};
|
||||
|
||||
|
|
|
@ -28,7 +28,13 @@
|
|||
"fast-glob": "^3.3.3",
|
||||
"minimatch": "^10.0.0",
|
||||
"shell-quote": "^1.8.2",
|
||||
"strip-ansi": "^7.1.0"
|
||||
"strip-ansi": "^7.1.0",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@opentelemetry/sdk-node": "^0.52.0",
|
||||
"@opentelemetry/exporter-trace-otlp-grpc": "^0.52.0",
|
||||
"@opentelemetry/exporter-logs-otlp-grpc": "^0.52.0",
|
||||
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.52.0",
|
||||
"@opentelemetry/instrumentation-http": "^0.52.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/diff": "^7.0.2",
|
||||
|
|
|
@ -48,6 +48,7 @@ describe('Server Config (config.ts)', () => {
|
|||
const FULL_CONTEXT = false;
|
||||
const USER_AGENT = 'ServerTestAgent/1.0';
|
||||
const USER_MEMORY = 'Test User Memory';
|
||||
const TELEMETRY = false;
|
||||
const baseParams: ConfigParameters = {
|
||||
apiKey: API_KEY,
|
||||
model: MODEL,
|
||||
|
@ -58,6 +59,7 @@ describe('Server Config (config.ts)', () => {
|
|||
fullContext: FULL_CONTEXT,
|
||||
userAgent: USER_AGENT,
|
||||
userMemory: USER_MEMORY,
|
||||
telemetry: TELEMETRY,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -161,6 +163,56 @@ describe('Server Config (config.ts)', () => {
|
|||
expect(config.getFileFilteringAllowBuildArtifacts()).toBe(true);
|
||||
});
|
||||
|
||||
it('Config constructor should set telemetry to true when provided as true', () => {
|
||||
const paramsWithTelemetry: ConfigParameters = {
|
||||
...baseParams,
|
||||
telemetry: true,
|
||||
};
|
||||
const config = new Config(paramsWithTelemetry);
|
||||
expect(config.getTelemetryEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('Config constructor should set telemetry to false when provided as false', () => {
|
||||
const paramsWithTelemetry: ConfigParameters = {
|
||||
...baseParams,
|
||||
telemetry: false,
|
||||
};
|
||||
const config = new Config(paramsWithTelemetry);
|
||||
expect(config.getTelemetryEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('Config constructor should default telemetry to default value if not provided', () => {
|
||||
const paramsWithoutTelemetry: ConfigParameters = { ...baseParams };
|
||||
delete paramsWithoutTelemetry.telemetry;
|
||||
const config = new Config(paramsWithoutTelemetry);
|
||||
expect(config.getTelemetryEnabled()).toBe(TELEMETRY);
|
||||
});
|
||||
|
||||
it('createServerConfig should pass telemetry to Config constructor when true', () => {
|
||||
const paramsWithTelemetry: ConfigParameters = {
|
||||
...baseParams,
|
||||
telemetry: true,
|
||||
};
|
||||
const config = createServerConfig(paramsWithTelemetry);
|
||||
expect(config.getTelemetryEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it('createServerConfig should pass telemetry to Config constructor when false', () => {
|
||||
const paramsWithTelemetry: ConfigParameters = {
|
||||
...baseParams,
|
||||
telemetry: false,
|
||||
};
|
||||
const config = createServerConfig(paramsWithTelemetry);
|
||||
expect(config.getTelemetryEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it('createServerConfig should default telemetry (to false via Config constructor) if omitted', () => {
|
||||
const paramsWithoutTelemetry: ConfigParameters = { ...baseParams };
|
||||
delete paramsWithoutTelemetry.telemetry;
|
||||
const config = createServerConfig(paramsWithoutTelemetry);
|
||||
expect(config.getTelemetryEnabled()).toBe(TELEMETRY);
|
||||
});
|
||||
|
||||
it('should have a getFileService method that returns FileDiscoveryService', async () => {
|
||||
const config = new Config(baseParams);
|
||||
const fileService = await config.getFileService();
|
||||
|
|
|
@ -24,6 +24,7 @@ import { WebSearchTool } from '../tools/web-search.js';
|
|||
import { GeminiClient } from '../core/client.js';
|
||||
import { GEMINI_CONFIG_DIR as GEMINI_DIR } from '../tools/memoryTool.js';
|
||||
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
|
||||
import { initializeTelemetry } from '../telemetry/index.js';
|
||||
|
||||
export enum ApprovalMode {
|
||||
DEFAULT = 'default',
|
||||
|
@ -72,6 +73,8 @@ export interface ConfigParameters {
|
|||
contextFileName?: string;
|
||||
geminiIgnorePatterns?: string[];
|
||||
accessibility?: AccessibilitySettings;
|
||||
telemetry?: boolean;
|
||||
telemetryLogUserPromptsEnabled?: boolean;
|
||||
fileFilteringRespectGitIgnore?: boolean;
|
||||
fileFilteringAllowBuildArtifacts?: boolean;
|
||||
}
|
||||
|
@ -97,6 +100,9 @@ export class Config {
|
|||
private readonly vertexai: boolean | undefined;
|
||||
private readonly showMemoryUsage: boolean;
|
||||
private readonly accessibility: AccessibilitySettings;
|
||||
private readonly telemetry: boolean;
|
||||
private readonly telemetryLogUserPromptsEnabled: boolean;
|
||||
private readonly telemetryOtlpEndpoint: string;
|
||||
private readonly geminiClient: GeminiClient;
|
||||
private readonly geminiIgnorePatterns: string[] = [];
|
||||
private readonly fileFilteringRespectGitIgnore: boolean;
|
||||
|
@ -123,6 +129,11 @@ export class Config {
|
|||
this.vertexai = params.vertexai;
|
||||
this.showMemoryUsage = params.showMemoryUsage ?? false;
|
||||
this.accessibility = params.accessibility ?? {};
|
||||
this.telemetry = params.telemetry ?? false;
|
||||
this.telemetryLogUserPromptsEnabled =
|
||||
params.telemetryLogUserPromptsEnabled ?? true;
|
||||
this.telemetryOtlpEndpoint =
|
||||
process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? 'http://localhost:4317';
|
||||
this.fileFilteringRespectGitIgnore =
|
||||
params.fileFilteringRespectGitIgnore ?? true;
|
||||
this.fileFilteringAllowBuildArtifacts =
|
||||
|
@ -137,6 +148,10 @@ export class Config {
|
|||
|
||||
this.toolRegistry = createToolRegistry(this);
|
||||
this.geminiClient = new GeminiClient(this);
|
||||
|
||||
if (this.telemetry) {
|
||||
initializeTelemetry(this);
|
||||
}
|
||||
}
|
||||
|
||||
getApiKey(): string {
|
||||
|
@ -230,6 +245,18 @@ export class Config {
|
|||
return this.accessibility;
|
||||
}
|
||||
|
||||
getTelemetryEnabled(): boolean {
|
||||
return this.telemetry;
|
||||
}
|
||||
|
||||
getTelemetryLogUserPromptsEnabled(): boolean {
|
||||
return this.telemetryLogUserPromptsEnabled;
|
||||
}
|
||||
|
||||
getTelemetryOtlpEndpoint(): string {
|
||||
return this.telemetryOtlpEndpoint;
|
||||
}
|
||||
|
||||
getGeminiClient(): GeminiClient {
|
||||
return this.geminiClient;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,11 @@ import { GeminiChat } from './geminiChat.js';
|
|||
import { retryWithBackoff } from '../utils/retry.js';
|
||||
import { getErrorMessage } from '../utils/errors.js';
|
||||
import { tokenLimit } from './tokenLimits.js';
|
||||
import {
|
||||
logApiRequest,
|
||||
logApiResponse,
|
||||
logApiError,
|
||||
} from '../telemetry/index.js';
|
||||
|
||||
export class GeminiClient {
|
||||
private chat: Promise<GeminiChat>;
|
||||
|
@ -192,6 +197,74 @@ export class GeminiClient {
|
|||
}
|
||||
}
|
||||
|
||||
private _logApiRequest(model: string, inputTokenCount: number): void {
|
||||
logApiRequest({
|
||||
model,
|
||||
prompt_token_count: inputTokenCount,
|
||||
duration_ms: 0, // Duration is not known at request time
|
||||
});
|
||||
}
|
||||
|
||||
private _logApiResponse(
|
||||
model: string,
|
||||
durationMs: number,
|
||||
attempt: number,
|
||||
response: GenerateContentResponse,
|
||||
): void {
|
||||
const promptFeedback = response.promptFeedback;
|
||||
const finishReason = response.candidates?.[0]?.finishReason;
|
||||
let responseError;
|
||||
if (promptFeedback?.blockReason) {
|
||||
responseError = `Blocked: ${promptFeedback.blockReason}${promptFeedback.blockReasonMessage ? ' - ' + promptFeedback.blockReasonMessage : ''}`;
|
||||
} else if (
|
||||
finishReason &&
|
||||
!['STOP', 'MAX_TOKENS', 'UNSPECIFIED'].includes(finishReason)
|
||||
) {
|
||||
responseError = `Finished with reason: ${finishReason}`;
|
||||
}
|
||||
|
||||
logApiResponse({
|
||||
model,
|
||||
duration_ms: durationMs,
|
||||
attempt,
|
||||
status_code: undefined,
|
||||
error: responseError,
|
||||
});
|
||||
}
|
||||
|
||||
private _logApiError(
|
||||
model: string,
|
||||
error: unknown,
|
||||
durationMs: number,
|
||||
attempt: number,
|
||||
isAbort: boolean = false,
|
||||
): void {
|
||||
let statusCode: number | string | undefined;
|
||||
let errorMessage = getErrorMessage(error);
|
||||
|
||||
if (isAbort) {
|
||||
errorMessage = 'Request aborted by user';
|
||||
statusCode = 'ABORTED'; // Custom S
|
||||
} else if (typeof error === 'object' && error !== null) {
|
||||
if ('status' in error) {
|
||||
statusCode = (error as { status: number | string }).status;
|
||||
} else if ('code' in error) {
|
||||
statusCode = (error as { code: number | string }).code;
|
||||
} else if ('httpStatusCode' in error) {
|
||||
statusCode = (error as { httpStatusCode: number | string })
|
||||
.httpStatusCode;
|
||||
}
|
||||
}
|
||||
|
||||
logApiError({
|
||||
model,
|
||||
error: errorMessage,
|
||||
status_code: statusCode,
|
||||
duration_ms: durationMs,
|
||||
attempt,
|
||||
});
|
||||
}
|
||||
|
||||
async generateJson(
|
||||
contents: Content[],
|
||||
schema: SchemaUnion,
|
||||
|
@ -199,6 +272,8 @@ export class GeminiClient {
|
|||
model: string = 'gemini-2.0-flash',
|
||||
config: GenerateContentConfig = {},
|
||||
): Promise<Record<string, unknown>> {
|
||||
const attempt = 1;
|
||||
const startTime = Date.now();
|
||||
try {
|
||||
const userMemory = this.config.getUserMemory();
|
||||
const systemInstruction = getCoreSystemPrompt(userMemory);
|
||||
|
@ -208,6 +283,22 @@ export class GeminiClient {
|
|||
...config,
|
||||
};
|
||||
|
||||
let inputTokenCount = 0;
|
||||
try {
|
||||
const { totalTokens } = await this.client.models.countTokens({
|
||||
model,
|
||||
contents,
|
||||
});
|
||||
inputTokenCount = totalTokens || 0;
|
||||
} catch (_e) {
|
||||
console.warn(
|
||||
`Failed to count tokens for model ${model}. Proceeding with inputTokenCount = 0. Error: ${getErrorMessage(_e)}`,
|
||||
);
|
||||
inputTokenCount = 0;
|
||||
}
|
||||
|
||||
this._logApiRequest(model, inputTokenCount);
|
||||
|
||||
const apiCall = () =>
|
||||
this.client.models.generateContent({
|
||||
model,
|
||||
|
@ -221,6 +312,7 @@ export class GeminiClient {
|
|||
});
|
||||
|
||||
const result = await retryWithBackoff(apiCall);
|
||||
const durationMs = Date.now() - startTime;
|
||||
|
||||
const text = getResponseText(result);
|
||||
if (!text) {
|
||||
|
@ -233,10 +325,13 @@ export class GeminiClient {
|
|||
contents,
|
||||
'generateJson-empty-response',
|
||||
);
|
||||
this._logApiError(model, error, durationMs, attempt);
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
const parsedJson = JSON.parse(text);
|
||||
this._logApiResponse(model, durationMs, attempt, result);
|
||||
return parsedJson;
|
||||
} catch (parseError) {
|
||||
await reportError(
|
||||
parseError,
|
||||
|
@ -247,13 +342,15 @@ export class GeminiClient {
|
|||
},
|
||||
'generateJson-parse',
|
||||
);
|
||||
this._logApiError(model, parseError, durationMs, attempt);
|
||||
throw new Error(
|
||||
`Failed to parse API response as JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
|
||||
`Failed to parse API response as JSON: ${getErrorMessage(parseError)}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
const durationMs = Date.now() - startTime;
|
||||
if (abortSignal.aborted) {
|
||||
// Regular cancellation error, fail normally
|
||||
this._logApiError(model, error, durationMs, attempt, true);
|
||||
throw error;
|
||||
}
|
||||
|
||||
|
@ -264,15 +361,17 @@ export class GeminiClient {
|
|||
) {
|
||||
throw error;
|
||||
}
|
||||
this._logApiError(model, error, durationMs, attempt);
|
||||
|
||||
await reportError(
|
||||
error,
|
||||
'Error generating JSON content via API.',
|
||||
contents,
|
||||
'generateJson-api',
|
||||
);
|
||||
const message =
|
||||
error instanceof Error ? error.message : 'Unknown API error.';
|
||||
throw new Error(`Failed to generate JSON content: ${message}`);
|
||||
throw new Error(
|
||||
`Failed to generate JSON content: ${getErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,6 +385,8 @@ export class GeminiClient {
|
|||
...this.generateContentConfig,
|
||||
...generationConfig,
|
||||
};
|
||||
const attempt = 1;
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const userMemory = this.config.getUserMemory();
|
||||
|
@ -297,6 +398,22 @@ export class GeminiClient {
|
|||
systemInstruction,
|
||||
};
|
||||
|
||||
let inputTokenCount = 0;
|
||||
try {
|
||||
const { totalTokens } = await this.client.models.countTokens({
|
||||
model: modelToUse,
|
||||
contents,
|
||||
});
|
||||
inputTokenCount = totalTokens || 0;
|
||||
} catch (_e) {
|
||||
console.warn(
|
||||
`Failed to count tokens for model ${modelToUse}. Proceeding with inputTokenCount = 0. Error: ${getErrorMessage(_e)}`,
|
||||
);
|
||||
inputTokenCount = 0;
|
||||
}
|
||||
|
||||
this._logApiRequest(modelToUse, inputTokenCount);
|
||||
|
||||
const apiCall = () =>
|
||||
this.client.models.generateContent({
|
||||
model: modelToUse,
|
||||
|
@ -305,12 +422,18 @@ export class GeminiClient {
|
|||
});
|
||||
|
||||
const result = await retryWithBackoff(apiCall);
|
||||
const durationMs = Date.now() - startTime;
|
||||
this._logApiResponse(modelToUse, durationMs, attempt, result);
|
||||
return result;
|
||||
} catch (error) {
|
||||
} catch (error: unknown) {
|
||||
const durationMs = Date.now() - startTime;
|
||||
if (abortSignal.aborted) {
|
||||
this._logApiError(modelToUse, error, durationMs, attempt, true);
|
||||
throw error;
|
||||
}
|
||||
|
||||
this._logApiError(modelToUse, error, durationMs, attempt);
|
||||
|
||||
await reportError(
|
||||
error,
|
||||
`Error generating content via API with model ${modelToUse}.`,
|
||||
|
@ -320,10 +443,8 @@ export class GeminiClient {
|
|||
},
|
||||
'generateContent-api',
|
||||
);
|
||||
const message =
|
||||
error instanceof Error ? error.message : 'Unknown API error.';
|
||||
throw new Error(
|
||||
`Failed to generate content with model ${modelToUse}: ${message}`,
|
||||
`Failed to generate content with model ${modelToUse}: ${getErrorMessage(error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,18 +21,21 @@ export type ValidatingToolCall = {
|
|||
status: 'validating';
|
||||
request: ToolCallRequestInfo;
|
||||
tool: Tool;
|
||||
startTime?: number;
|
||||
};
|
||||
|
||||
export type ScheduledToolCall = {
|
||||
status: 'scheduled';
|
||||
request: ToolCallRequestInfo;
|
||||
tool: Tool;
|
||||
startTime?: number;
|
||||
};
|
||||
|
||||
export type ErroredToolCall = {
|
||||
status: 'error';
|
||||
request: ToolCallRequestInfo;
|
||||
response: ToolCallResponseInfo;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
export type SuccessfulToolCall = {
|
||||
|
@ -40,6 +43,7 @@ export type SuccessfulToolCall = {
|
|||
request: ToolCallRequestInfo;
|
||||
tool: Tool;
|
||||
response: ToolCallResponseInfo;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
export type ExecutingToolCall = {
|
||||
|
@ -47,6 +51,7 @@ export type ExecutingToolCall = {
|
|||
request: ToolCallRequestInfo;
|
||||
tool: Tool;
|
||||
liveOutput?: string;
|
||||
startTime?: number;
|
||||
};
|
||||
|
||||
export type CancelledToolCall = {
|
||||
|
@ -54,6 +59,7 @@ export type CancelledToolCall = {
|
|||
request: ToolCallRequestInfo;
|
||||
response: ToolCallResponseInfo;
|
||||
tool: Tool;
|
||||
durationMs?: number;
|
||||
};
|
||||
|
||||
export type WaitingToolCall = {
|
||||
|
@ -61,6 +67,7 @@ export type WaitingToolCall = {
|
|||
request: ToolCallRequestInfo;
|
||||
tool: Tool;
|
||||
confirmationDetails: ToolCallConfirmationDetails;
|
||||
startTime?: number;
|
||||
};
|
||||
|
||||
export type Status = ToolCall['status'];
|
||||
|
@ -246,40 +253,69 @@ export class CoreToolScheduler {
|
|||
this.toolCalls = this.toolCalls.map((currentCall) => {
|
||||
if (
|
||||
currentCall.request.callId !== targetCallId ||
|
||||
currentCall.status === 'error'
|
||||
currentCall.status === 'success' ||
|
||||
currentCall.status === 'error' ||
|
||||
currentCall.status === 'cancelled'
|
||||
) {
|
||||
return currentCall;
|
||||
}
|
||||
|
||||
const callWithToolContext = currentCall as ToolCall & { tool: Tool };
|
||||
// currentCall is a non-terminal state here and should have startTime and tool.
|
||||
const existingStartTime = currentCall.startTime;
|
||||
const toolInstance = (
|
||||
currentCall as
|
||||
| ValidatingToolCall
|
||||
| ScheduledToolCall
|
||||
| ExecutingToolCall
|
||||
| WaitingToolCall
|
||||
).tool;
|
||||
|
||||
switch (newStatus) {
|
||||
case 'success':
|
||||
case 'success': {
|
||||
const durationMs = existingStartTime
|
||||
? Date.now() - existingStartTime
|
||||
: undefined;
|
||||
return {
|
||||
...callWithToolContext,
|
||||
request: currentCall.request,
|
||||
tool: toolInstance,
|
||||
status: 'success',
|
||||
response: auxiliaryData as ToolCallResponseInfo,
|
||||
durationMs,
|
||||
} as SuccessfulToolCall;
|
||||
case 'error':
|
||||
}
|
||||
case 'error': {
|
||||
const durationMs = existingStartTime
|
||||
? Date.now() - existingStartTime
|
||||
: undefined;
|
||||
return {
|
||||
request: currentCall.request,
|
||||
status: 'error',
|
||||
response: auxiliaryData as ToolCallResponseInfo,
|
||||
durationMs,
|
||||
} as ErroredToolCall;
|
||||
}
|
||||
case 'awaiting_approval':
|
||||
return {
|
||||
...callWithToolContext,
|
||||
request: currentCall.request,
|
||||
tool: toolInstance,
|
||||
status: 'awaiting_approval',
|
||||
confirmationDetails: auxiliaryData as ToolCallConfirmationDetails,
|
||||
startTime: existingStartTime,
|
||||
} as WaitingToolCall;
|
||||
case 'scheduled':
|
||||
return {
|
||||
...callWithToolContext,
|
||||
request: currentCall.request,
|
||||
tool: toolInstance,
|
||||
status: 'scheduled',
|
||||
startTime: existingStartTime,
|
||||
} as ScheduledToolCall;
|
||||
case 'cancelled':
|
||||
case 'cancelled': {
|
||||
const durationMs = existingStartTime
|
||||
? Date.now() - existingStartTime
|
||||
: undefined;
|
||||
return {
|
||||
...callWithToolContext,
|
||||
request: currentCall.request,
|
||||
tool: toolInstance,
|
||||
status: 'cancelled',
|
||||
response: {
|
||||
callId: currentCall.request.callId,
|
||||
|
@ -295,16 +331,22 @@ export class CoreToolScheduler {
|
|||
resultDisplay: undefined,
|
||||
error: undefined,
|
||||
},
|
||||
durationMs,
|
||||
} as CancelledToolCall;
|
||||
}
|
||||
case 'validating':
|
||||
return {
|
||||
...(currentCall as ValidatingToolCall),
|
||||
request: currentCall.request,
|
||||
tool: toolInstance,
|
||||
status: 'validating',
|
||||
startTime: existingStartTime,
|
||||
} as ValidatingToolCall;
|
||||
case 'executing':
|
||||
return {
|
||||
...callWithToolContext,
|
||||
request: currentCall.request,
|
||||
tool: toolInstance,
|
||||
status: 'executing',
|
||||
startTime: existingStartTime,
|
||||
} as ExecutingToolCall;
|
||||
default: {
|
||||
const exhaustiveCheck: never = newStatus;
|
||||
|
@ -345,9 +387,15 @@ export class CoreToolScheduler {
|
|||
reqInfo,
|
||||
new Error(`Tool "${reqInfo.name}" not found in registry.`),
|
||||
),
|
||||
durationMs: 0,
|
||||
};
|
||||
}
|
||||
return { status: 'validating', request: reqInfo, tool: toolInstance };
|
||||
return {
|
||||
status: 'validating',
|
||||
request: reqInfo,
|
||||
tool: toolInstance,
|
||||
startTime: Date.now(),
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
@ -44,3 +44,6 @@ export * from './tools/memoryTool.js';
|
|||
export * from './tools/shell.js';
|
||||
export * from './tools/web-search.js';
|
||||
export * from './tools/read-many-files.js';
|
||||
|
||||
// Export telemetry functions
|
||||
export * from './telemetry/index.js';
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { randomUUID } from 'crypto';
|
||||
|
||||
export const SERVICE_NAME = 'gemini-code';
|
||||
export const sessionId = randomUUID();
|
||||
|
||||
export const EVENT_USER_PROMPT = 'gemini_code.user_prompt';
|
||||
export const EVENT_TOOL_CALL = 'gemini_code.tool_call';
|
||||
export const EVENT_API_REQUEST = 'gemini_code.api_request';
|
||||
export const EVENT_API_ERROR = 'gemini_code.api_error';
|
||||
export const EVENT_API_RESPONSE = 'gemini_code.api_response';
|
||||
export const EVENT_CLI_CONFIG = 'gemini_code.config';
|
||||
|
||||
export const METRIC_TOOL_CALL_COUNT = 'gemini_code.tool.call.count';
|
||||
export const METRIC_TOOL_CALL_LATENCY = 'gemini_code.tool.call.latency';
|
||||
export const METRIC_API_REQUEST_COUNT = 'gemini_code.api.request.count';
|
||||
export const METRIC_API_REQUEST_LATENCY = 'gemini_code.api.request.latency';
|
||||
export const METRIC_TOKEN_INPUT_COUNT = 'gemini_code.token.input.count';
|
||||
export const METRIC_SESSION_COUNT = 'gemini_code.session.count';
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
export {
|
||||
initializeTelemetry,
|
||||
shutdownTelemetry,
|
||||
isTelemetrySdkInitialized,
|
||||
} from './sdk.js';
|
||||
export {
|
||||
logCliConfiguration,
|
||||
logUserPrompt,
|
||||
logToolCall,
|
||||
logApiRequest,
|
||||
logApiError,
|
||||
logApiResponse,
|
||||
} from './loggers.js';
|
||||
export {
|
||||
UserPromptEvent,
|
||||
ToolCallEvent,
|
||||
ApiRequestEvent,
|
||||
ApiErrorEvent,
|
||||
ApiResponseEvent,
|
||||
CliConfigEvent,
|
||||
TelemetryEvent,
|
||||
} from './types.js';
|
||||
export { SpanStatusCode, ValueType } from '@opentelemetry/api';
|
||||
export { SemanticAttributes } from '@opentelemetry/semantic-conventions';
|
||||
export { sessionId } from './constants.js';
|
|
@ -0,0 +1,191 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { logs, LogRecord, LogAttributes } from '@opentelemetry/api-logs';
|
||||
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
|
||||
import { Config } from '../config/config.js';
|
||||
import {
|
||||
EVENT_API_ERROR,
|
||||
EVENT_API_REQUEST,
|
||||
EVENT_API_RESPONSE,
|
||||
EVENT_CLI_CONFIG,
|
||||
EVENT_TOOL_CALL,
|
||||
EVENT_USER_PROMPT,
|
||||
SERVICE_NAME,
|
||||
} from './constants.js';
|
||||
import {
|
||||
ApiErrorEvent,
|
||||
ApiRequestEvent,
|
||||
ApiResponseEvent,
|
||||
ToolCallEvent,
|
||||
UserPromptEvent,
|
||||
} from './types.js';
|
||||
import {
|
||||
recordApiErrorMetrics,
|
||||
recordApiRequestMetrics,
|
||||
recordApiResponseMetrics,
|
||||
recordToolCallMetrics,
|
||||
} from './metrics.js';
|
||||
import { isTelemetrySdkInitialized } from './sdk.js';
|
||||
|
||||
const shouldLogUserPrompts = (config: Config): boolean =>
|
||||
config.getTelemetryLogUserPromptsEnabled() ?? false;
|
||||
|
||||
export function logCliConfiguration(config: Config): void {
|
||||
if (!isTelemetrySdkInitialized()) return;
|
||||
|
||||
const attributes: LogAttributes = {
|
||||
'event.name': EVENT_CLI_CONFIG,
|
||||
'event.timestamp': new Date().toISOString(),
|
||||
model: config.getModel(),
|
||||
sandbox_enabled:
|
||||
typeof config.getSandbox() === 'string' ? true : config.getSandbox(),
|
||||
core_tools_enabled: (config.getCoreTools() ?? []).join(','),
|
||||
approval_mode: config.getApprovalMode(),
|
||||
vertex_ai_enabled: config.getVertexAI() ?? false,
|
||||
log_user_prompts_enabled: config.getTelemetryLogUserPromptsEnabled(),
|
||||
file_filtering_respect_git_ignore:
|
||||
config.getFileFilteringRespectGitIgnore(),
|
||||
file_filtering_allow_build_artifacts:
|
||||
config.getFileFilteringAllowBuildArtifacts(),
|
||||
};
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: 'CLI configuration loaded.',
|
||||
attributes,
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
}
|
||||
|
||||
export function logUserPrompt(
|
||||
config: Config,
|
||||
event: Omit<UserPromptEvent, 'event.name' | 'event.timestamp' | 'prompt'> & {
|
||||
prompt: string;
|
||||
},
|
||||
): void {
|
||||
if (!isTelemetrySdkInitialized()) return;
|
||||
const { prompt, ...restOfEventArgs } = event;
|
||||
const attributes: LogAttributes = {
|
||||
...restOfEventArgs,
|
||||
'event.name': EVENT_USER_PROMPT,
|
||||
'event.timestamp': new Date().toISOString(),
|
||||
};
|
||||
if (shouldLogUserPrompts(config)) {
|
||||
attributes.prompt = prompt;
|
||||
}
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: `User prompt. Length: ${event.prompt_char_count}`,
|
||||
attributes,
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
}
|
||||
|
||||
export function logToolCall(
|
||||
event: Omit<ToolCallEvent, 'event.name' | 'event.timestamp'>,
|
||||
): void {
|
||||
if (!isTelemetrySdkInitialized()) return;
|
||||
const attributes: LogAttributes = {
|
||||
...event,
|
||||
'event.name': EVENT_TOOL_CALL,
|
||||
'event.timestamp': new Date().toISOString(),
|
||||
function_args: JSON.stringify(event.function_args),
|
||||
};
|
||||
if (event.error) {
|
||||
attributes['error.message'] = event.error;
|
||||
if (event.error_type) {
|
||||
attributes['error.type'] = event.error_type;
|
||||
}
|
||||
}
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: `Tool call: ${event.function_name}. Success: ${event.success}. Duration: ${event.duration_ms}ms.`,
|
||||
attributes,
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
recordToolCallMetrics(event.function_name, event.duration_ms, event.success);
|
||||
}
|
||||
|
||||
export function logApiRequest(
|
||||
event: Omit<ApiRequestEvent, 'event.name' | 'event.timestamp'>,
|
||||
): void {
|
||||
if (!isTelemetrySdkInitialized()) return;
|
||||
const attributes: LogAttributes = {
|
||||
...event,
|
||||
'event.name': EVENT_API_REQUEST,
|
||||
'event.timestamp': new Date().toISOString(),
|
||||
};
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: `API request to ${event.model}. Tokens: ${event.prompt_token_count}.`,
|
||||
attributes,
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
recordApiRequestMetrics(event.model, event.prompt_token_count);
|
||||
}
|
||||
|
||||
export function logApiError(
|
||||
event: Omit<ApiErrorEvent, 'event.name' | 'event.timestamp'>,
|
||||
): void {
|
||||
if (!isTelemetrySdkInitialized()) return;
|
||||
const attributes: LogAttributes = {
|
||||
...event,
|
||||
'event.name': EVENT_API_ERROR,
|
||||
'event.timestamp': new Date().toISOString(),
|
||||
['error.message']: event.error,
|
||||
};
|
||||
|
||||
if (event.error_type) {
|
||||
attributes['error.type'] = event.error_type;
|
||||
}
|
||||
if (typeof event.status_code === 'number') {
|
||||
attributes[SemanticAttributes.HTTP_STATUS_CODE] = event.status_code;
|
||||
}
|
||||
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: `API error for ${event.model}. Error: ${event.error}. Duration: ${event.duration_ms}ms.`,
|
||||
attributes,
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
recordApiErrorMetrics(
|
||||
event.model,
|
||||
event.duration_ms,
|
||||
event.status_code,
|
||||
event.error_type,
|
||||
);
|
||||
}
|
||||
|
||||
export function logApiResponse(
|
||||
event: Omit<ApiResponseEvent, 'event.name' | 'event.timestamp'>,
|
||||
): void {
|
||||
if (!isTelemetrySdkInitialized()) return;
|
||||
const attributes: LogAttributes = {
|
||||
...event,
|
||||
'event.name': EVENT_API_RESPONSE,
|
||||
'event.timestamp': new Date().toISOString(),
|
||||
};
|
||||
if (event.error) {
|
||||
attributes['error.message'] = event.error;
|
||||
} else if (event.status_code) {
|
||||
if (typeof event.status_code === 'number') {
|
||||
attributes[SemanticAttributes.HTTP_STATUS_CODE] = event.status_code;
|
||||
}
|
||||
}
|
||||
|
||||
const logger = logs.getLogger(SERVICE_NAME);
|
||||
const logRecord: LogRecord = {
|
||||
body: `API response from ${event.model}. Status: ${event.status_code || 'N/A'}. Duration: ${event.duration_ms}ms.`,
|
||||
attributes,
|
||||
};
|
||||
logger.emit(logRecord);
|
||||
recordApiResponseMetrics(
|
||||
event.model,
|
||||
event.duration_ms,
|
||||
event.status_code,
|
||||
event.error,
|
||||
);
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
metrics,
|
||||
Attributes,
|
||||
ValueType,
|
||||
Meter,
|
||||
Counter,
|
||||
Histogram,
|
||||
} from '@opentelemetry/api';
|
||||
import {
|
||||
SERVICE_NAME,
|
||||
METRIC_TOOL_CALL_COUNT,
|
||||
METRIC_TOOL_CALL_LATENCY,
|
||||
METRIC_API_REQUEST_COUNT,
|
||||
METRIC_API_REQUEST_LATENCY,
|
||||
METRIC_TOKEN_INPUT_COUNT,
|
||||
METRIC_SESSION_COUNT,
|
||||
} from './constants.js';
|
||||
|
||||
let cliMeter: Meter | undefined;
|
||||
let toolCallCounter: Counter | undefined;
|
||||
let toolCallLatencyHistogram: Histogram | undefined;
|
||||
let apiRequestCounter: Counter | undefined;
|
||||
let apiRequestLatencyHistogram: Histogram | undefined;
|
||||
let tokenInputCounter: Counter | undefined;
|
||||
let isMetricsInitialized = false;
|
||||
|
||||
export function getMeter(): Meter | undefined {
|
||||
if (!cliMeter) {
|
||||
cliMeter = metrics.getMeter(SERVICE_NAME);
|
||||
}
|
||||
return cliMeter;
|
||||
}
|
||||
|
||||
export function initializeMetrics(): void {
|
||||
if (isMetricsInitialized) return;
|
||||
|
||||
const meter = getMeter();
|
||||
if (!meter) return;
|
||||
|
||||
toolCallCounter = meter.createCounter(METRIC_TOOL_CALL_COUNT, {
|
||||
description: 'Counts tool calls, tagged by function name and success.',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
toolCallLatencyHistogram = meter.createHistogram(METRIC_TOOL_CALL_LATENCY, {
|
||||
description: 'Latency of tool calls in milliseconds.',
|
||||
unit: 'ms',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
apiRequestCounter = meter.createCounter(METRIC_API_REQUEST_COUNT, {
|
||||
description: 'Counts API requests, tagged by model and status.',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
apiRequestLatencyHistogram = meter.createHistogram(
|
||||
METRIC_API_REQUEST_LATENCY,
|
||||
{
|
||||
description: 'Latency of API requests in milliseconds.',
|
||||
unit: 'ms',
|
||||
valueType: ValueType.INT,
|
||||
},
|
||||
);
|
||||
tokenInputCounter = meter.createCounter(METRIC_TOKEN_INPUT_COUNT, {
|
||||
description: 'Counts the total number of input tokens sent to the API.',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
|
||||
const sessionCounter = meter.createCounter(METRIC_SESSION_COUNT, {
|
||||
description: 'Count of CLI sessions started.',
|
||||
valueType: ValueType.INT,
|
||||
});
|
||||
sessionCounter.add(1);
|
||||
isMetricsInitialized = true;
|
||||
}
|
||||
|
||||
export function recordToolCallMetrics(
|
||||
functionName: string,
|
||||
durationMs: number,
|
||||
success: boolean,
|
||||
): void {
|
||||
if (!toolCallCounter || !toolCallLatencyHistogram || !isMetricsInitialized)
|
||||
return;
|
||||
|
||||
const metricAttributes: Attributes = {
|
||||
function_name: functionName,
|
||||
success,
|
||||
};
|
||||
toolCallCounter.add(1, metricAttributes);
|
||||
toolCallLatencyHistogram.record(durationMs, {
|
||||
function_name: functionName,
|
||||
});
|
||||
}
|
||||
|
||||
export function recordApiRequestMetrics(
|
||||
model: string,
|
||||
inputTokenCount: number,
|
||||
): void {
|
||||
if (!tokenInputCounter || !isMetricsInitialized) return;
|
||||
tokenInputCounter.add(inputTokenCount, { model });
|
||||
}
|
||||
|
||||
export function recordApiResponseMetrics(
|
||||
model: string,
|
||||
durationMs: number,
|
||||
statusCode?: number | string,
|
||||
error?: string,
|
||||
): void {
|
||||
if (
|
||||
!apiRequestCounter ||
|
||||
!apiRequestLatencyHistogram ||
|
||||
!isMetricsInitialized
|
||||
)
|
||||
return;
|
||||
const metricAttributes: Attributes = {
|
||||
model,
|
||||
status_code: statusCode ?? (error ? 'error' : 'ok'),
|
||||
};
|
||||
apiRequestCounter.add(1, metricAttributes);
|
||||
apiRequestLatencyHistogram.record(durationMs, { model });
|
||||
}
|
||||
|
||||
export function recordApiErrorMetrics(
|
||||
model: string,
|
||||
durationMs: number,
|
||||
statusCode?: number | string,
|
||||
errorType?: string,
|
||||
): void {
|
||||
if (
|
||||
!apiRequestCounter ||
|
||||
!apiRequestLatencyHistogram ||
|
||||
!isMetricsInitialized
|
||||
)
|
||||
return;
|
||||
const metricAttributes: Attributes = {
|
||||
model,
|
||||
status_code: statusCode ?? 'error',
|
||||
error_type: errorType ?? 'unknown',
|
||||
};
|
||||
apiRequestCounter.add(1, metricAttributes);
|
||||
apiRequestLatencyHistogram.record(durationMs, { model });
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { DiagConsoleLogger, DiagLogLevel, diag } from '@opentelemetry/api';
|
||||
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
||||
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-grpc';
|
||||
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
||||
import { NodeSDK } from '@opentelemetry/sdk-node';
|
||||
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
||||
import { Resource } from '@opentelemetry/resources';
|
||||
import {
|
||||
BatchSpanProcessor,
|
||||
ConsoleSpanExporter,
|
||||
} from '@opentelemetry/sdk-trace-node';
|
||||
import {
|
||||
BatchLogRecordProcessor,
|
||||
ConsoleLogRecordExporter,
|
||||
} from '@opentelemetry/sdk-logs';
|
||||
import {
|
||||
ConsoleMetricExporter,
|
||||
PeriodicExportingMetricReader,
|
||||
} from '@opentelemetry/sdk-metrics';
|
||||
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
||||
import { Config } from '../config/config.js';
|
||||
import { SERVICE_NAME, sessionId } from './constants.js';
|
||||
import { initializeMetrics } from './metrics.js';
|
||||
import { logCliConfiguration } from './loggers.js';
|
||||
|
||||
// For troubleshooting, set the log level to DiagLogLevel.DEBUG
|
||||
diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.INFO);
|
||||
|
||||
let sdk: NodeSDK | undefined;
|
||||
let telemetryInitialized = false;
|
||||
|
||||
export function isTelemetrySdkInitialized(): boolean {
|
||||
return telemetryInitialized;
|
||||
}
|
||||
|
||||
function parseGrpcEndpoint(
|
||||
otlpEndpointSetting: string | undefined,
|
||||
): string | undefined {
|
||||
if (!otlpEndpointSetting) {
|
||||
return undefined;
|
||||
}
|
||||
// Trim leading/trailing quotes that might come from env variables
|
||||
const trimmedEndpoint = otlpEndpointSetting.replace(/^["']|["']$/g, '');
|
||||
|
||||
try {
|
||||
const url = new URL(trimmedEndpoint);
|
||||
// OTLP gRPC exporters expect an endpoint in the format scheme://host:port
|
||||
// The `origin` property provides this, stripping any path, query, or hash.
|
||||
return url.origin;
|
||||
} catch (error) {
|
||||
diag.error('Invalid OTLP endpoint URL provided:', trimmedEndpoint, error);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
export function initializeTelemetry(config: Config): void {
|
||||
if (telemetryInitialized || !config.getTelemetryEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const geminiCliVersion = config.getUserAgent() || 'unknown';
|
||||
const resource = new Resource({
|
||||
[SemanticResourceAttributes.SERVICE_NAME]: SERVICE_NAME,
|
||||
[SemanticResourceAttributes.SERVICE_VERSION]: geminiCliVersion,
|
||||
'session.id': sessionId,
|
||||
});
|
||||
|
||||
const otlpEndpoint = config.getTelemetryOtlpEndpoint();
|
||||
const grpcParsedEndpoint = parseGrpcEndpoint(otlpEndpoint);
|
||||
const useOtlp = !!grpcParsedEndpoint;
|
||||
|
||||
const spanExporter = useOtlp
|
||||
? new OTLPTraceExporter({ url: grpcParsedEndpoint })
|
||||
: new ConsoleSpanExporter();
|
||||
const logExporter = useOtlp
|
||||
? new OTLPLogExporter({ url: grpcParsedEndpoint })
|
||||
: new ConsoleLogRecordExporter();
|
||||
const metricReader = useOtlp
|
||||
? new PeriodicExportingMetricReader({
|
||||
exporter: new OTLPMetricExporter({ url: grpcParsedEndpoint }),
|
||||
exportIntervalMillis: 10000,
|
||||
})
|
||||
: new PeriodicExportingMetricReader({
|
||||
exporter: new ConsoleMetricExporter(),
|
||||
exportIntervalMillis: 10000,
|
||||
});
|
||||
|
||||
sdk = new NodeSDK({
|
||||
resource,
|
||||
spanProcessors: [new BatchSpanProcessor(spanExporter)],
|
||||
logRecordProcessor: new BatchLogRecordProcessor(logExporter),
|
||||
metricReader,
|
||||
instrumentations: [new HttpInstrumentation()],
|
||||
});
|
||||
|
||||
try {
|
||||
sdk.start();
|
||||
console.log('OpenTelemetry SDK started successfully.');
|
||||
telemetryInitialized = true;
|
||||
initializeMetrics();
|
||||
logCliConfiguration(config);
|
||||
} catch (error) {
|
||||
console.error('Error starting OpenTelemetry SDK:', error);
|
||||
}
|
||||
|
||||
process.on('SIGTERM', shutdownTelemetry);
|
||||
process.on('SIGINT', shutdownTelemetry);
|
||||
}
|
||||
|
||||
export async function shutdownTelemetry(): Promise<void> {
|
||||
if (!telemetryInitialized || !sdk) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await sdk.shutdown();
|
||||
console.log('OpenTelemetry SDK shut down successfully.');
|
||||
} catch (error) {
|
||||
console.error('Error shutting down SDK:', error);
|
||||
} finally {
|
||||
telemetryInitialized = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
export interface UserPromptEvent {
|
||||
'event.name': 'user_prompt';
|
||||
'event.timestamp': string; // ISO 8601
|
||||
prompt_char_count: number;
|
||||
prompt?: string;
|
||||
}
|
||||
|
||||
export interface ToolCallEvent {
|
||||
'event.name': 'tool_call';
|
||||
'event.timestamp': string; // ISO 8601
|
||||
function_name: string;
|
||||
function_args: Record<string, unknown>;
|
||||
duration_ms: number;
|
||||
success: boolean;
|
||||
error?: string;
|
||||
error_type?: string;
|
||||
}
|
||||
|
||||
export interface ApiRequestEvent {
|
||||
'event.name': 'api_request';
|
||||
'event.timestamp': string; // ISO 8601
|
||||
model: string;
|
||||
duration_ms: number;
|
||||
prompt_token_count: number;
|
||||
}
|
||||
|
||||
export interface ApiErrorEvent {
|
||||
'event.name': 'api_error';
|
||||
'event.timestamp': string; // ISO 8601
|
||||
model: string;
|
||||
error: string;
|
||||
error_type?: string;
|
||||
status_code?: number | string;
|
||||
duration_ms: number;
|
||||
attempt: number;
|
||||
}
|
||||
|
||||
export interface ApiResponseEvent {
|
||||
'event.name': 'api_response';
|
||||
'event.timestamp': string; // ISO 8601
|
||||
model: string;
|
||||
status_code?: number | string;
|
||||
duration_ms: number;
|
||||
error?: string;
|
||||
attempt: number;
|
||||
}
|
||||
|
||||
export interface CliConfigEvent {
|
||||
'event.name': 'cli_config';
|
||||
'event.timestamp': string; // ISO 8601
|
||||
model: string;
|
||||
sandbox_enabled: boolean;
|
||||
core_tools_enabled: string;
|
||||
approval_mode: string;
|
||||
vertex_ai_enabled: boolean;
|
||||
log_user_prompts_enabled: boolean;
|
||||
file_filtering_respect_git_ignore: boolean;
|
||||
file_filtering_allow_build_artifacts: boolean;
|
||||
}
|
||||
|
||||
export type TelemetryEvent =
|
||||
| UserPromptEvent
|
||||
| ToolCallEvent
|
||||
| ApiRequestEvent
|
||||
| ApiErrorEvent
|
||||
| ApiResponseEvent
|
||||
| CliConfigEvent;
|
Loading…
Reference in New Issue