Skip to content
Merged
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
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ dependencies = [
"sentry-ophio>=1.1.3",
"sentry-protos>=0.8.11",
"sentry-redis-tools>=0.5.0",
"sentry-relay>=0.9.26",
"sentry-relay>=0.9.27",
"sentry-sdk[http2]>=2.47.0",
"sentry-usage-accountant>=0.0.10",
# remove once there are no unmarked transitive dependencies on setuptools
Expand Down
5 changes: 5 additions & 0 deletions src/sentry/conf/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -1205,10 +1205,15 @@ def SOCIAL_AUTH_DEFAULT_USERNAME() -> str:
"task": "relocation:sentry.relocation.transfer.find_relocation_transfer_region",
"schedule": crontab("*/5", "*", "*", "*", "*"),
},
# TODO(constantinius): Remove fetch-ai-model-costs once all consumers have migrated to fetch-ai-model-metadata
"fetch-ai-model-costs": {
"task": "ai_agent_monitoring:sentry.tasks.ai_agent_monitoring.fetch_ai_model_costs",
"schedule": crontab("*/30", "*", "*", "*", "*"),
},
"fetch-ai-model-metadata": {
"task": "ai_agent_monitoring:sentry.tasks.ai_agent_monitoring.fetch_ai_model_metadata",
"schedule": crontab("*/30", "*", "*", "*", "*"),
},
"llm-issue-detection": {
"task": "issues:sentry.tasks.llm_issue_detection.run_llm_issue_detection",
"schedule": crontab("0", "*", "*", "*", "*"),
Expand Down
63 changes: 54 additions & 9 deletions src/sentry/relay/config/ai_model_costs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,20 @@
type ModelId = str


# Cache key for storing AI model costs
# Legacy cache key for AI model costs (v2 flat format)
# TODO(constantinius): Remove once all consumers have migrated to AI_MODEL_METADATA_CACHE_KEY
AI_MODEL_COSTS_CACHE_KEY = "ai-model-costs:v2"
# Cache timeout: 30 days (we re-fetch every 30 minutes, so this provides more than enough overlap)
AI_MODEL_COSTS_CACHE_TTL = 30 * 24 * 60 * 60

# Cache key for storing LLM model metadata (v1 nested format)
AI_MODEL_METADATA_CACHE_KEY = "ai-model-metadata:v1"
# Cache timeout: 30 days (we re-fetch every 30 minutes, so this provides more than enough overlap)
AI_MODEL_METADATA_CACHE_TTL = 30 * 24 * 60 * 60


class AIModelCostV2(TypedDict):
"""Legacy flat format. TODO(constantinius): Remove once all consumers have migrated."""

inputPerToken: float
outputPerToken: float
outputReasoningPerToken: float
Expand All @@ -26,18 +33,34 @@ class AIModelCostV2(TypedDict):


class AIModelCosts(TypedDict):
"""Legacy config type. TODO(constantinius): Remove once all consumers have migrated."""

version: Required[int]
models: Required[dict[ModelId, AIModelCostV2]]


class AIModelCost(TypedDict):
inputPerToken: float
outputPerToken: float
outputReasoningPerToken: float
inputCachedPerToken: float
inputCacheWritePerToken: float
Comment thread
cursor[bot] marked this conversation as resolved.


class AIModelMetadata(TypedDict, total=False):
costs: Required[AIModelCost]
contextSize: int


class AIModelMetadataConfig(TypedDict):
version: Required[int]
models: Required[dict[ModelId, AIModelMetadata]]


def ai_model_costs_config() -> AIModelCosts | None:
"""
Get AI model costs configuration.
AI model costs are set in cache by a cron job,
if there are no costs, it should be investigated why.

Returns:
AIModelCosts object containing cost information for AI models
Legacy: Get AI model costs configuration.
TODO(constantinius): Remove once all consumers have migrated to ai_model_metadata_config.
"""
if settings.SENTRY_AIR_GAP:
return None
Expand All @@ -47,7 +70,29 @@ def ai_model_costs_config() -> AIModelCosts | None:
return cached_costs

if not settings.IS_DEV:
# in dev environment, we don't want to log this
logger.warning("Empty model costs")

return None


def ai_model_metadata_config() -> AIModelMetadataConfig | None:
"""
Get LLM model metadata configuration.
LLM model metadata is set in cache by a cron job,
if there is no metadata, it should be investigated why.

Returns:
AIModelMetadataConfig containing cost and context size information for LLM models
"""
if settings.SENTRY_AIR_GAP:
return None

cached_metadata = cache.get(AI_MODEL_METADATA_CACHE_KEY)
if cached_metadata is not None:
return cached_metadata

if not settings.IS_DEV:
# in dev environment, we don't want to log this
logger.warning("Empty LLM model metadata")

return None
Comment thread
constantinius marked this conversation as resolved.
15 changes: 12 additions & 3 deletions src/sentry/relay/globalconfig.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
from typing import Any, TypedDict

import sentry.options
from sentry.relay.config.ai_model_costs import AIModelCosts, ai_model_costs_config
from sentry.relay.config.ai_model_costs import (
AIModelCosts,
AIModelMetadataConfig,
ai_model_costs_config,
ai_model_metadata_config,
)
from sentry.relay.config.measurements import MeasurementsConfig, get_measurements_config
from sentry.relay.config.metric_extraction import (
MetricExtractionGroups,
Expand Down Expand Up @@ -39,7 +44,10 @@ class SpanOpDefaults(TypedDict):

class GlobalConfig(TypedDict, total=False):
measurements: MeasurementsConfig
aiModelCosts: AIModelCosts | None
aiModelCosts: (
AIModelCosts | None
) # TODO(constantinius): Remove once all consumers use aiModelMetadata
aiModelMetadata: AIModelMetadataConfig | None
metricExtraction: MetricExtractionGroups
filters: GenericFiltersConfig | None
spanOpDefaults: SpanOpDefaults
Expand Down Expand Up @@ -78,7 +86,8 @@ def get_global_config() -> GlobalConfig:

global_config: GlobalConfig = {
"measurements": get_measurements_config(),
"aiModelCosts": ai_model_costs_config(),
"aiModelCosts": ai_model_costs_config(), # TODO(constantinius): Remove once all consumers use aiModelMetadata
"aiModelMetadata": ai_model_metadata_config(),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Was this schema already added to Relay?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There is a PR in Relay for that: getsentry/relay#5814

"metricExtraction": global_metric_extraction_groups(),
"spanOpDefaults": span_op_defaults(),
}
Expand Down
Loading
Loading