Skip to content

Commit 0fe5bfb

Browse files
authored
Merge pull request #2473 from anik120/spans-for-discovery-catalog-endpoints
LCORE-1793: Add OTEL spans for discovery and catalog endpoints
2 parents 0e3eabd + 82ef541 commit 0fe5bfb

10 files changed

Lines changed: 701 additions & 148 deletions

File tree

src/app/endpoints/models.py

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from fastapi import APIRouter, HTTPException, Query, Request
66
from fastapi.params import Depends
77
from ogx_client import APIConnectionError
8+
from opentelemetry import trace
89

910
from authentication import get_auth_dependency
1011
from authentication.interface import AuthTuple
@@ -26,6 +27,7 @@
2627
from utils.model_list import parse_model_list_response
2728

2829
logger = get_logger(__name__)
30+
tracer = trace.get_tracer(__name__)
2931
router = APIRouter(tags=["models"])
3032

3133

@@ -84,29 +86,31 @@ async def models_endpoint_handler(
8486
# Nothing interesting in the request
8587
_ = request
8688

87-
check_configuration_loaded(configuration)
88-
89-
llama_stack_configuration = configuration.llama_stack_configuration
90-
logger.info("Llama Stack config: %s", llama_stack_configuration)
91-
92-
try:
93-
# try to get Llama Stack client
94-
client = AsyncOgxClientHolder().get_client()
95-
# retrieve and normalize models across OpenAI/Anthropic/Google list shapes
96-
parsed_models = parse_model_list_response(await client.models.list())
97-
98-
# optional filtering by model type
99-
if model_type.model_type is not None:
100-
parsed_models = [
101-
model
102-
for model in parsed_models
103-
if model.model_type == model_type.model_type
104-
]
105-
106-
return ModelsResponse(models=parsed_models)
107-
108-
# Connection to Llama Stack server failed
109-
except APIConnectionError as e:
110-
logger.error("Unable to connect to Llama Stack: %s", e)
111-
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
112-
raise HTTPException(**response.model_dump()) from e
89+
with tracer.start_as_current_span("models.list") as span:
90+
check_configuration_loaded(configuration)
91+
92+
llama_stack_configuration = configuration.llama_stack_configuration
93+
logger.info("Llama Stack config: %s", llama_stack_configuration)
94+
95+
try:
96+
# try to get Llama Stack client
97+
client = AsyncOgxClientHolder().get_client()
98+
# retrieve and normalize models across OpenAI/Anthropic/Google list shapes
99+
parsed_models = parse_model_list_response(await client.models.list())
100+
101+
# optional filtering by model type
102+
if model_type.model_type is not None:
103+
parsed_models = [
104+
model
105+
for model in parsed_models
106+
if model.model_type == model_type.model_type
107+
]
108+
109+
span.set_attribute("models.count", len(parsed_models))
110+
return ModelsResponse(models=parsed_models)
111+
112+
# Connection to Llama Stack server failed
113+
except APIConnectionError as e:
114+
logger.error("Unable to connect to Llama Stack: %s", e)
115+
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
116+
raise HTTPException(**response.model_dump()) from e

src/app/endpoints/providers.py

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from fastapi.params import Depends
77
from ogx_client import APIConnectionError, BadRequestError
88
from ogx_client.types import ProviderListResponse
9+
from opentelemetry import trace
910

1011
from authentication import get_auth_dependency
1112
from authentication.interface import AuthTuple
@@ -29,6 +30,7 @@
2930
from utils.endpoints import check_configuration_loaded
3031

3132
logger = get_logger(__name__)
33+
tracer = trace.get_tracer(__name__)
3234
router = APIRouter(tags=["providers"])
3335

3436

@@ -84,20 +86,22 @@ async def providers_endpoint_handler(
8486
# Nothing interesting in the request
8587
_ = request
8688

87-
check_configuration_loaded(configuration)
89+
with tracer.start_as_current_span("providers.list") as span:
90+
check_configuration_loaded(configuration)
8891

89-
llama_stack_configuration = configuration.llama_stack_configuration
90-
logger.info("Llama Stack config: %s", llama_stack_configuration)
92+
llama_stack_configuration = configuration.llama_stack_configuration
93+
logger.info("Llama Stack config: %s", llama_stack_configuration)
9194

92-
try:
93-
client = AsyncOgxClientHolder().get_client()
94-
providers: ProviderListResponse = await client.providers.list()
95-
except APIConnectionError as e:
96-
logger.error("Unable to connect to Llama Stack: %s", e)
97-
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
98-
raise HTTPException(**response.model_dump()) from e
95+
try:
96+
client = AsyncOgxClientHolder().get_client()
97+
providers: ProviderListResponse = await client.providers.list()
98+
except APIConnectionError as e:
99+
logger.error("Unable to connect to Llama Stack: %s", e)
100+
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
101+
raise HTTPException(**response.model_dump()) from e
99102

100-
return ProvidersListResponse(providers=group_providers(providers))
103+
span.set_attribute("providers.count", len(providers))
104+
return ProvidersListResponse(providers=group_providers(providers))
101105

102106

103107
def group_providers(providers: ProviderListResponse) -> dict[str, list[dict[str, Any]]]:
@@ -154,21 +158,23 @@ async def get_provider_endpoint_handler(
154158
# Nothing interesting in the request
155159
_ = request
156160

157-
check_configuration_loaded(configuration)
161+
with tracer.start_as_current_span("providers.get") as span:
162+
check_configuration_loaded(configuration)
158163

159-
llama_stack_configuration = configuration.llama_stack_configuration
160-
logger.info("Llama Stack config: %s", llama_stack_configuration)
164+
llama_stack_configuration = configuration.llama_stack_configuration
165+
logger.info("Llama Stack config: %s", llama_stack_configuration)
161166

162-
try:
163-
client = AsyncOgxClientHolder().get_client()
164-
provider = await client.providers.retrieve(provider_id)
165-
return ProviderResponse(**provider.model_dump())
167+
try:
168+
client = AsyncOgxClientHolder().get_client()
169+
provider = await client.providers.retrieve(provider_id)
170+
span.set_attribute("providers.found", True)
171+
return ProviderResponse(**provider.model_dump())
166172

167-
except APIConnectionError as e:
168-
logger.error("Unable to connect to Llama Stack: %s", e)
169-
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
170-
raise HTTPException(**response.model_dump()) from e
173+
except APIConnectionError as e:
174+
logger.error("Unable to connect to Llama Stack: %s", e)
175+
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
176+
raise HTTPException(**response.model_dump()) from e
171177

172-
except BadRequestError as e:
173-
response = NotFoundResponse(resource="provider", resource_id=provider_id)
174-
raise HTTPException(**response.model_dump()) from e
178+
except BadRequestError as e:
179+
response = NotFoundResponse(resource="provider", resource_id=provider_id)
180+
raise HTTPException(**response.model_dump()) from e

src/app/endpoints/rags.py

Lines changed: 63 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from fastapi import APIRouter, HTTPException, Request
66
from fastapi.params import Depends
77
from ogx_client import APIConnectionError, BadRequestError
8+
from opentelemetry import trace
89

910
from authentication import get_auth_dependency
1011
from authentication.interface import AuthTuple
@@ -28,6 +29,7 @@
2829
from utils.endpoints import check_configuration_loaded
2930

3031
logger = get_logger(__name__)
32+
tracer = trace.get_tracer(__name__)
3133
router = APIRouter(tags=["rags"])
3234

3335

@@ -83,33 +85,35 @@ async def rags_endpoint_handler(
8385
# Nothing interesting in the request
8486
_ = request
8587

86-
# make sure that the configuration is loaded
87-
check_configuration_loaded(configuration)
88+
with tracer.start_as_current_span("rags.list") as span:
89+
# make sure that the configuration is loaded
90+
check_configuration_loaded(configuration)
8891

89-
llama_stack_configuration = configuration.llama_stack_configuration
90-
logger.info("Llama Stack config: %s", llama_stack_configuration)
92+
llama_stack_configuration = configuration.llama_stack_configuration
93+
logger.info("Llama Stack config: %s", llama_stack_configuration)
9194

92-
try:
93-
# try to get Llama Stack client
94-
client = AsyncOgxClientHolder().get_client()
95-
# retrieve list of RAGs
96-
rags = await client.vector_stores.list()
97-
logger.info("List of rags: %d", len(rags.data))
95+
try:
96+
# try to get Llama Stack client
97+
client = AsyncOgxClientHolder().get_client()
98+
# retrieve list of RAGs
99+
rags = await client.vector_stores.list()
100+
logger.info("List of rags: %d", len(rags.data))
98101

99-
# Map llama-stack vector store IDs to user-facing rag_ids from config
100-
rag_id_mapping = configuration.rag_id_mapping
101-
rag_ids = [
102-
configuration.resolve_index_name(rag.id, rag_id_mapping)
103-
for rag in rags.data
104-
]
102+
# Map llama-stack vector store IDs to user-facing rag_ids from config
103+
rag_id_mapping = configuration.rag_id_mapping
104+
rag_ids = [
105+
configuration.resolve_index_name(rag.id, rag_id_mapping)
106+
for rag in rags.data
107+
]
105108

106-
return RAGListResponse(rags=rag_ids)
109+
span.set_attribute("rags.count", len(rag_ids))
110+
return RAGListResponse(rags=rag_ids)
107111

108-
# connection to Llama Stack server
109-
except APIConnectionError as e:
110-
logger.error("Unable to connect to Llama Stack: %s", e)
111-
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
112-
raise HTTPException(**response.model_dump()) from e
112+
# connection to Llama Stack server
113+
except APIConnectionError as e:
114+
logger.error("Unable to connect to Llama Stack: %s", e)
115+
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
116+
raise HTTPException(**response.model_dump()) from e
113117

114118

115119
def _resolve_rag_id_to_vector_db_id(rag_id: str, byok_rags: list[RagStore]) -> str:
@@ -171,42 +175,44 @@ async def get_rag_endpoint_handler(
171175
# Nothing interesting in the request
172176
_ = request
173177

174-
check_configuration_loaded(configuration)
178+
with tracer.start_as_current_span("rags.get") as span:
179+
check_configuration_loaded(configuration)
175180

176-
llama_stack_configuration = configuration.llama_stack_configuration
177-
logger.info("Llama Stack config: %s", llama_stack_configuration)
181+
llama_stack_configuration = configuration.llama_stack_configuration
182+
logger.info("Llama Stack config: %s", llama_stack_configuration)
178183

179-
# Resolve user-facing rag_id to llama-stack vector_db_id
180-
vector_db_id = _resolve_rag_id_to_vector_db_id(
181-
rag_id, configuration.configuration.rag.byok.stores
182-
)
183-
184-
try:
185-
# try to get Llama Stack client
186-
client = AsyncOgxClientHolder().get_client()
187-
# retrieve info about RAG
188-
rag_info = await client.vector_stores.retrieve(vector_db_id)
189-
190-
# Return the user-facing ID (rag_id from config if mapped, otherwise as-is)
191-
display_id = configuration.resolve_index_name(
192-
rag_info.id, configuration.rag_id_mapping
184+
# Resolve user-facing rag_id to llama-stack vector_db_id
185+
vector_db_id = _resolve_rag_id_to_vector_db_id(
186+
rag_id, configuration.configuration.rag.byok.stores
193187
)
194188

195-
return RAGInfoResponse(
196-
id=display_id,
197-
name=rag_info.name,
198-
created_at=rag_info.created_at,
199-
last_active_at=rag_info.last_active_at,
200-
expires_at=rag_info.expires_at,
201-
object=rag_info.object or "vector_store",
202-
status=rag_info.status or "unknown",
203-
usage_bytes=rag_info.usage_bytes or 0,
204-
)
205-
except APIConnectionError as e:
206-
logger.error("Unable to connect to Llama Stack: %s", e)
207-
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
208-
raise HTTPException(**response.model_dump()) from e
209-
except BadRequestError as e:
210-
logger.error("RAG not found: %s", e)
211-
response = NotFoundResponse(resource="rag", resource_id=rag_id)
212-
raise HTTPException(**response.model_dump()) from e
189+
try:
190+
# try to get Llama Stack client
191+
client = AsyncOgxClientHolder().get_client()
192+
# retrieve info about RAG
193+
rag_info = await client.vector_stores.retrieve(vector_db_id)
194+
195+
# Return the user-facing ID (rag_id from config if mapped, otherwise as-is)
196+
display_id = configuration.resolve_index_name(
197+
rag_info.id, configuration.rag_id_mapping
198+
)
199+
200+
span.set_attribute("rags.found", True)
201+
return RAGInfoResponse(
202+
id=display_id,
203+
name=rag_info.name,
204+
created_at=rag_info.created_at,
205+
last_active_at=rag_info.last_active_at,
206+
expires_at=rag_info.expires_at,
207+
object=rag_info.object or "vector_store",
208+
status=rag_info.status or "unknown",
209+
usage_bytes=rag_info.usage_bytes or 0,
210+
)
211+
except APIConnectionError as e:
212+
logger.error("Unable to connect to Llama Stack: %s", e)
213+
response = ServiceUnavailableResponse(backend_name="OGX", cause=str(e))
214+
raise HTTPException(**response.model_dump()) from e
215+
except BadRequestError as e:
216+
logger.error("RAG not found: %s", e)
217+
response = NotFoundResponse(resource="rag", resource_id=rag_id)
218+
raise HTTPException(**response.model_dump()) from e

src/app/endpoints/shields.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from fastapi import APIRouter, Request
66
from fastapi.params import Depends
7+
from opentelemetry import trace
78

89
from authentication import get_auth_dependency
910
from authentication.interface import AuthTuple
@@ -22,6 +23,7 @@
2223
from utils.endpoints import check_configuration_loaded
2324

2425
logger = get_logger(__name__)
26+
tracer = trace.get_tracer(__name__)
2527
router = APIRouter(tags=["shields"])
2628

2729

@@ -64,11 +66,13 @@ async def shields_endpoint_handler(
6466
# Nothing interesting in the request
6567
_ = request
6668

67-
check_configuration_loaded(configuration)
69+
with tracer.start_as_current_span("shields.list") as span:
70+
check_configuration_loaded(configuration)
6871

69-
shields = [
70-
CatalogShield.model_validate(shield.model_dump())
71-
for shield in configuration.shields
72-
]
73-
logger.info("Returning %d configured shield(s)", len(shields))
74-
return ShieldsResponse(shields=shields)
72+
shields = [
73+
CatalogShield.model_validate(shield.model_dump())
74+
for shield in configuration.shields
75+
]
76+
logger.info("Returning %d configured shield(s)", len(shields))
77+
span.set_attribute("shields.count", len(shields))
78+
return ShieldsResponse(shields=shields)

0 commit comments

Comments
 (0)