Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


general:
telemetry:
logging:
console:
_type: console
level: WARN
file:
_type: file
path: ./.tmp/nat_simple_calculator.log
level: DEBUG
tracing:
futureagi:
_type: futureagi
endpoint: https://api.futureagi.com/tracer/v1/traces
project: simple_calculator
Copy link
Member

Choose a reason for hiding this comment

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

project is not part of the futureagi configuration?

fi_api_key: ${FI_API_KEY}
fi_secret_key: ${FI_SECRET_KEY}
project_version_name: "1.0.0"

functions:
calculator_multiply:
_type: calculator_multiply
calculator_inequality:
_type: calculator_inequality
calculator_divide:
_type: nat_simple_calculator/calculator_divide
current_datetime:
_type: current_datetime
calculator_subtract:
_type: calculator_subtract

llms:
nim_llm:
_type: nim
model_name: meta/llama-3.1-70b-instruct
temperature: 0.0
max_tokens: 1024
openai_llm:
_type: openai
model_name: gpt-3.5-turbo
max_tokens: 2000

workflow:
_type: react_agent
tool_names:
- calculator_multiply
- calculator_inequality
- current_datetime
- calculator_divide
- calculator_subtract
llm_name: nim_llm
verbose: true
parse_agent_response_max_retries: 3
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import logging
import os
from typing import Any

from pydantic import Field

Expand Down Expand Up @@ -192,3 +193,55 @@ async def galileo_telemetry_exporter(config: GalileoTelemetryExporter, builder:
drop_on_overflow=config.drop_on_overflow,
shutdown_timeout=config.shutdown_timeout,
)


class FutureAGITelemetryExporter(BatchConfigMixin, CollectorConfigMixin, TelemetryExporterBaseConfig, name="futureagi"):
"""A telemetry exporter to transmit traces to Future AGI service."""

endpoint: str = Field(description="The Future AGI endpoint to export telemetry traces",
default="https://api.futureagi.com/tracer/v1/traces")
fi_api_key: str = Field(description="The FutureAGI API key", default="")
fi_secret_key: str = Field(description="The FutureAGI Secret key", default="")
project_type: str = Field(description="The project type for FutureAGI instrumentation", default="observe")
project_version_name: str = Field(description="The project version name", default="1.0.0")
resource_attributes: dict[str, Any] = Field(default_factory=dict,
description="The resource attributes to add to the span")
Comment on lines +198 to +208
Copy link
Member

@willkill07 willkill07 Oct 23, 2025

Choose a reason for hiding this comment

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

project is not a field here. The config data model must be updated.



@register_telemetry_exporter(config_type=FutureAGITelemetryExporter)
async def futureagi_telemetry_exporter(config: FutureAGITelemetryExporter, builder: Builder):
"""Create a FutureAGI telemetry exporter."""
import json
import uuid

from nat.plugins.opentelemetry import OTLPSpanAdapterExporter

api_key = config.fi_api_key or os.environ.get("FI_API_KEY")
secret_key = config.fi_secret_key or os.environ.get("FI_SECRET_KEY")

if not api_key or not secret_key:
raise ValueError("API key and Secret key are required for FutureAGI")

headers = {"x-api-key": api_key, "x-secret-key": secret_key}

project_version_id = str(uuid.uuid4())
Copy link
Member

@willkill07 willkill07 Oct 23, 2025

Choose a reason for hiding this comment

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

You have project_version_id as a config member. Use it?

uuid should not be required


default_resource_attributes = {
"project_name": config.project,
Copy link
Member

Choose a reason for hiding this comment

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

currently fails to run. config.project does not exist.

"project_type": config.project_type.lower(),
"project_version_name": config.project_version_name,
"project_version_id": project_version_id,
"eval_tags": json.dumps([]),
"metadata": json.dumps({})
Comment on lines +234 to +235
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"eval_tags": json.dumps([]),
"metadata": json.dumps({})
"eval_tags": [],
"metadata": {}

}

merged_resource_attributes = {**default_resource_attributes, **config.resource_attributes}
Copy link

@coderabbitai coderabbitai bot Oct 23, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

User-provided attributes can override critical fields.

The merge strategy at line 238 allows user-provided resource_attributes to override system-generated defaults like project_name, project_type, and project_version_id, which could break FutureAGI's tracing.

Reverse the merge order to give precedence to critical system attributes:

-    merged_resource_attributes = {**default_resource_attributes, **config.resource_attributes}
+    # Merge user attributes first, then overlay critical system attributes
+    merged_resource_attributes = {**config.resource_attributes, **default_resource_attributes}

Or more explicitly protect critical fields:

+    # Start with user attributes
+    merged_resource_attributes = dict(config.resource_attributes)
+    
+    # Override with critical system attributes that must not be changed
+    merged_resource_attributes.update({
+        "project_name": config.project,
+        "project_type": config.project_type.lower(),
+        "project_version_name": config.project_version_name,
+        "project_version_id": project_version_id,
+    })
+    
+    # Add optional attributes only if not already present
+    merged_resource_attributes.setdefault("eval_tags", json.dumps([]))
+    merged_resource_attributes.setdefault("metadata", json.dumps({}))
-
-    merged_resource_attributes = {**default_resource_attributes, **config.resource_attributes}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
merged_resource_attributes = {**default_resource_attributes, **config.resource_attributes}
# Merge user attributes first, then overlay critical system attributes
merged_resource_attributes = {**config.resource_attributes, **default_resource_attributes}
🤖 Prompt for AI Agents
In packages/nvidia_nat_opentelemetry/src/nat/plugins/opentelemetry/register.py
around line 238, the current merge lets user-supplied config.resource_attributes
overwrite critical system defaults; change the merge so system defaults win
(e.g., merged_resource_attributes = {**config.resource_attributes,
**default_resource_attributes}) or explicitly remove/protect keys like
"project_name", "project_type", and "project_version_id" from
config.resource_attributes before merging so those critical fields cannot be
overridden by user input.

Copy link
Member

Choose a reason for hiding this comment

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

This is a critical issue that needs addressed

Copy link

Choose a reason for hiding this comment

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

@willkill07, I agree this is critical. Allowing user-provided resource_attributes to override system-generated fields like project_name, project_type, and project_version_id could break FutureAGI's tracing functionality.

Here's the recommended fix that explicitly protects critical system attributes:

    # Start with user-provided attributes
    merged_resource_attributes = dict(config.resource_attributes)
    
    # Override with critical system attributes that must not be changed
    merged_resource_attributes.update({
        "project_name": config.project,
        "project_type": config.project_type.lower(),
        "project_version_name": config.project_version_name,
        "project_version_id": project_version_id,
    })
    
    # Add optional attributes only if not already present
    merged_resource_attributes.setdefault("eval_tags", json.dumps([]))
    merged_resource_attributes.setdefault("metadata", json.dumps({}))

This approach:

  1. Starts with user attributes to preserve their custom fields
  2. Explicitly overwrites the 4 critical fields that FutureAGI requires
  3. Uses setdefault() for eval_tags and metadata to allow user override while providing sensible defaults

This makes the protection of critical fields explicit and maintainable. The current line should be removed and replaced with the above code block.


yield OTLPSpanAdapterExporter(endpoint=config.endpoint,
headers=headers,
resource_attributes=merged_resource_attributes,
batch_size=config.batch_size,
flush_interval=config.flush_interval,
max_queue_size=config.max_queue_size,
drop_on_overflow=config.drop_on_overflow,
shutdown_timeout=config.shutdown_timeout)