feat(backend): Simplify caching to just store routes (#11140)

### Problem
Limits caching to just the main marketplace routes

### Changes 🏗️

- **Simplified store cache implementation** in
`backend/server/v2/store/cache.py`
  - Streamlined caching logic for better maintainability
  - Reduced complexity while maintaining performance
  
- **Added cache invalidation on store updates**
  - Implemented cache clearing when new agents are added to the store
- Added invalidation logic in admin store routes
(`admin_store_routes.py`)
  - Ensures all pods reflect the latest store state after modifications

- **Updated store database operations** in
`backend/server/v2/store/db.py`
  - Modified to work with the new cache structure
  
- **Added cache deletion tests** (`test_cache_delete.py`)
  - Validates cache invalidation works correctly
  - Ensures cache consistency across operations

### Checklist 📋

#### For code changes:
- [x] I have clearly listed my changes in the PR description
- [x] I have made a test plan
- [x] I have tested my changes according to the test plan:
  - [x] Verify store listings are cached correctly
  - [x] Upload a new agent to the store and confirm cache is invalidated
This commit is contained in:
Swifty
2025-10-13 09:25:59 +02:00
committed by GitHub
parent 20acd8b51d
commit e32c509ccc
6 changed files with 159 additions and 286 deletions

View File

@@ -7,6 +7,7 @@ import fastapi
import fastapi.responses
import prisma.enums
import backend.server.v2.store.cache as store_cache
import backend.server.v2.store.db
import backend.server.v2.store.model
import backend.util.json
@@ -86,6 +87,11 @@ async def review_submission(
StoreSubmission with updated review information
"""
try:
already_approved = (
await backend.server.v2.store.db.check_submission_already_approved(
store_listing_version_id=store_listing_version_id,
)
)
submission = await backend.server.v2.store.db.review_store_submission(
store_listing_version_id=store_listing_version_id,
is_approved=request.is_approved,
@@ -93,6 +99,11 @@ async def review_submission(
internal_comments=request.internal_comments or "",
reviewer_id=user_id,
)
state_changed = already_approved != request.is_approved
# Clear caches when the request is approved as it updates what is shown on the store
if state_changed:
store_cache.clear_all_caches()
return submission
except Exception as e:
logger.exception("Error reviewing submission: %s", e)

View File

@@ -0,0 +1,76 @@
from autogpt_libs.utils.cache import cached
import backend.server.v2.store.db
##############################################
############### Caches #######################
##############################################
def clear_all_caches():
"""Clear all caches."""
_get_cached_store_agents.cache_clear()
_get_cached_agent_details.cache_clear()
_get_cached_store_creators.cache_clear()
_get_cached_creator_details.cache_clear()
# Cache store agents list for 5 minutes
# Different cache entries for different query combinations
@cached(maxsize=5000, ttl_seconds=300)
async def _get_cached_store_agents(
featured: bool,
creator: str | None,
sorted_by: str | None,
search_query: str | None,
category: str | None,
page: int,
page_size: int,
):
"""Cached helper to get store agents."""
return await backend.server.v2.store.db.get_store_agents(
featured=featured,
creators=[creator] if creator else None,
sorted_by=sorted_by,
search_query=search_query,
category=category,
page=page,
page_size=page_size,
)
# Cache individual agent details for 15 minutes
@cached(maxsize=200, ttl_seconds=300)
async def _get_cached_agent_details(username: str, agent_name: str):
"""Cached helper to get agent details."""
return await backend.server.v2.store.db.get_store_agent_details(
username=username, agent_name=agent_name
)
# Cache creators list for 5 minutes
@cached(maxsize=200, ttl_seconds=300)
async def _get_cached_store_creators(
featured: bool,
search_query: str | None,
sorted_by: str | None,
page: int,
page_size: int,
):
"""Cached helper to get store creators."""
return await backend.server.v2.store.db.get_store_creators(
featured=featured,
search_query=search_query,
sorted_by=sorted_by,
page=page,
page_size=page_size,
)
# Cache individual creator details for 5 minutes
@cached(maxsize=100, ttl_seconds=300)
async def _get_cached_creator_details(username: str):
"""Cached helper to get creator details."""
return await backend.server.v2.store.db.get_store_creator_details(
username=username.lower()
)

View File

@@ -1806,6 +1806,27 @@ async def get_admin_listings_with_versions(
)
async def check_submission_already_approved(
store_listing_version_id: str,
) -> bool:
"""Check the submission status of a store listing version."""
try:
store_listing_version = (
await prisma.models.StoreListingVersion.prisma().find_unique(
where={"id": store_listing_version_id}
)
)
if not store_listing_version:
return False
return (
store_listing_version.submissionStatus
== prisma.enums.SubmissionStatus.APPROVED
)
except Exception as e:
logger.error(f"Error checking submission status: {e}")
return False
async def get_agent_as_admin(
user_id: str | None,
store_listing_version_id: str,

View File

@@ -6,9 +6,9 @@ import urllib.parse
import autogpt_libs.auth
import fastapi
import fastapi.responses
from autogpt_libs.utils.cache import cached
import backend.data.graph
import backend.server.v2.store.cache as store_cache
import backend.server.v2.store.db
import backend.server.v2.store.exceptions
import backend.server.v2.store.image_gen
@@ -21,117 +21,6 @@ logger = logging.getLogger(__name__)
router = fastapi.APIRouter()
##############################################
############### Caches #######################
##############################################
# Cache user profiles for 1 hour per user
@cached(maxsize=1000, ttl_seconds=3600)
async def _get_cached_user_profile(user_id: str):
"""Cached helper to get user profile."""
return await backend.server.v2.store.db.get_user_profile(user_id)
# Cache store agents list for 15 minutes
# Different cache entries for different query combinations
@cached(maxsize=5000, ttl_seconds=900)
async def _get_cached_store_agents(
featured: bool,
creator: str | None,
sorted_by: str | None,
search_query: str | None,
category: str | None,
page: int,
page_size: int,
):
"""Cached helper to get store agents."""
return await backend.server.v2.store.db.get_store_agents(
featured=featured,
creators=[creator] if creator else None,
sorted_by=sorted_by,
search_query=search_query,
category=category,
page=page,
page_size=page_size,
)
# Cache individual agent details for 15 minutes
@cached(maxsize=200, ttl_seconds=900)
async def _get_cached_agent_details(username: str, agent_name: str):
"""Cached helper to get agent details."""
return await backend.server.v2.store.db.get_store_agent_details(
username=username, agent_name=agent_name
)
# Cache agent graphs for 1 hour
@cached(maxsize=200, ttl_seconds=3600)
async def _get_cached_agent_graph(store_listing_version_id: str):
"""Cached helper to get agent graph."""
return await backend.server.v2.store.db.get_available_graph(
store_listing_version_id
)
# Cache agent by version for 1 hour
@cached(maxsize=200, ttl_seconds=3600)
async def _get_cached_store_agent_by_version(store_listing_version_id: str):
"""Cached helper to get store agent by version ID."""
return await backend.server.v2.store.db.get_store_agent_by_version_id(
store_listing_version_id
)
# Cache creators list for 1 hour
@cached(maxsize=200, ttl_seconds=3600)
async def _get_cached_store_creators(
featured: bool,
search_query: str | None,
sorted_by: str | None,
page: int,
page_size: int,
):
"""Cached helper to get store creators."""
return await backend.server.v2.store.db.get_store_creators(
featured=featured,
search_query=search_query,
sorted_by=sorted_by,
page=page,
page_size=page_size,
)
# Cache individual creator details for 1 hour
@cached(maxsize=100, ttl_seconds=3600)
async def _get_cached_creator_details(username: str):
"""Cached helper to get creator details."""
return await backend.server.v2.store.db.get_store_creator_details(
username=username.lower()
)
# Cache user's own agents for 5 mins (shorter TTL as this changes more frequently)
@cached(maxsize=500, ttl_seconds=300)
async def _get_cached_my_agents(user_id: str, page: int, page_size: int):
"""Cached helper to get user's agents."""
return await backend.server.v2.store.db.get_my_agents(
user_id, page=page, page_size=page_size
)
# Cache user's submissions for 1 hour (shorter TTL as this changes frequently)
@cached(maxsize=500, ttl_seconds=3600)
async def _get_cached_submissions(user_id: str, page: int, page_size: int):
"""Cached helper to get user's submissions."""
return await backend.server.v2.store.db.get_store_submissions(
user_id=user_id,
page=page,
page_size=page_size,
)
##############################################
############### Profile Endpoints ############
##############################################
@@ -152,7 +41,7 @@ async def get_profile(
Cached for 1 hour per user.
"""
try:
profile = await _get_cached_user_profile(user_id)
profile = await backend.server.v2.store.db.get_user_profile(user_id)
if profile is None:
return fastapi.responses.JSONResponse(
status_code=404,
@@ -198,8 +87,6 @@ async def update_or_create_profile(
updated_profile = await backend.server.v2.store.db.update_profile(
user_id=user_id, profile=profile
)
# Clear the cache for this user after profile update
_get_cached_user_profile.cache_delete(user_id)
return updated_profile
except Exception as e:
logger.exception("Failed to update profile for user %s: %s", user_id, e)
@@ -234,7 +121,6 @@ async def get_agents(
):
"""
Get a paginated list of agents from the store with optional filtering and sorting.
Results are cached for 15 minutes.
Args:
featured (bool, optional): Filter to only show featured agents. Defaults to False.
@@ -270,7 +156,7 @@ async def get_agents(
)
try:
agents = await _get_cached_store_agents(
agents = await store_cache._get_cached_store_agents(
featured=featured,
creator=creator,
sorted_by=sorted_by,
@@ -300,7 +186,6 @@ async def get_agents(
async def get_agent(username: str, agent_name: str):
"""
This is only used on the AgentDetails Page.
Results are cached for 15 minutes.
It returns the store listing agents details.
"""
@@ -308,7 +193,7 @@ async def get_agent(username: str, agent_name: str):
username = urllib.parse.unquote(username).lower()
# URL decode the agent name since it comes from the URL path
agent_name = urllib.parse.unquote(agent_name).lower()
agent = await _get_cached_agent_details(
agent = await store_cache._get_cached_agent_details(
username=username, agent_name=agent_name
)
return agent
@@ -331,10 +216,11 @@ async def get_agent(username: str, agent_name: str):
async def get_graph_meta_by_store_listing_version_id(store_listing_version_id: str):
"""
Get Agent Graph from Store Listing Version ID.
Results are cached for 1 hour.
"""
try:
graph = await _get_cached_agent_graph(store_listing_version_id)
graph = await backend.server.v2.store.db.get_available_graph(
store_listing_version_id
)
return graph
except Exception:
logger.exception("Exception occurred whilst getting agent graph")
@@ -354,10 +240,12 @@ async def get_graph_meta_by_store_listing_version_id(store_listing_version_id: s
async def get_store_agent(store_listing_version_id: str):
"""
Get Store Agent Details from Store Listing Version ID.
Results are cached for 1 hour.
"""
try:
agent = await _get_cached_store_agent_by_version(store_listing_version_id)
agent = await backend.server.v2.store.db.get_store_agent_by_version_id(
store_listing_version_id
)
return agent
except Exception:
logger.exception("Exception occurred whilst getting store agent")
@@ -435,8 +323,6 @@ async def get_creators(
- Home Page Featured Creators
- Search Results Page
Results are cached for 1 hour.
---
To support this functionality we need:
@@ -455,7 +341,7 @@ async def get_creators(
)
try:
creators = await _get_cached_store_creators(
creators = await store_cache._get_cached_store_creators(
featured=featured,
search_query=search_query,
sorted_by=sorted_by,
@@ -482,12 +368,11 @@ async def get_creator(
):
"""
Get the details of a creator.
Results are cached for 1 hour.
- Creator Details Page
"""
try:
username = urllib.parse.unquote(username).lower()
creator = await _get_cached_creator_details(username=username)
creator = await store_cache._get_cached_creator_details(username=username)
return creator
except Exception:
logger.exception("Exception occurred whilst getting creator details")
@@ -518,10 +403,11 @@ async def get_my_agents(
):
"""
Get user's own agents.
Results are cached for 5 minutes per user.
"""
try:
agents = await _get_cached_my_agents(user_id, page=page, page_size=page_size)
agents = await backend.server.v2.store.db.get_my_agents(
user_id, page=page, page_size=page_size
)
return agents
except Exception:
logger.exception("Exception occurred whilst getting my agents")
@@ -558,13 +444,6 @@ async def delete_submission(
submission_id=submission_id,
)
# Clear submissions cache for this specific user after deletion
if result:
# Clear user's own agents cache - we don't know all page/size combinations
for page in range(1, 20):
# Clear user's submissions cache for common defaults
_get_cached_submissions.cache_delete(user_id, page=page, page_size=20)
return result
except Exception:
logger.exception("Exception occurred whilst deleting store submission")
@@ -588,7 +467,6 @@ async def get_submissions(
):
"""
Get a paginated list of store submissions for the authenticated user.
Results are cached for 1 hour per user.
Args:
user_id (str): ID of the authenticated user
@@ -611,8 +489,10 @@ async def get_submissions(
status_code=422, detail="Page size must be greater than 0"
)
try:
listings = await _get_cached_submissions(
user_id, page=page, page_size=page_size
listings = await backend.server.v2.store.db.get_store_submissions(
user_id=user_id,
page=page,
page_size=page_size,
)
return listings
except Exception:
@@ -666,11 +546,6 @@ async def create_submission(
recommended_schedule_cron=submission_request.recommended_schedule_cron,
)
# Clear user's own agents cache - we don't know all page/size combinations
for page in range(1, 20):
# Clear user's submissions cache for common defaults
_get_cached_submissions.cache_delete(user_id, page=page, page_size=20)
return result
except Exception:
logger.exception("Exception occurred whilst creating store submission")
@@ -720,11 +595,6 @@ async def edit_submission(
recommended_schedule_cron=submission_request.recommended_schedule_cron,
)
# Clear user's own agents cache - we don't know all page/size combinations
for page in range(1, 20):
# Clear user's submissions cache for common defaults
_get_cached_submissions.cache_delete(user_id, page=page, page_size=20)
return result
@@ -917,15 +787,10 @@ async def get_cache_metrics():
)
# Add metrics for each cache
add_cache_metrics("user_profile", _get_cached_user_profile)
add_cache_metrics("store_agents", _get_cached_store_agents)
add_cache_metrics("agent_details", _get_cached_agent_details)
add_cache_metrics("agent_graph", _get_cached_agent_graph)
add_cache_metrics("agent_by_version", _get_cached_store_agent_by_version)
add_cache_metrics("store_creators", _get_cached_store_creators)
add_cache_metrics("creator_details", _get_cached_creator_details)
add_cache_metrics("my_agents", _get_cached_my_agents)
add_cache_metrics("submissions", _get_cached_submissions)
add_cache_metrics("store_agents", store_cache._get_cached_store_agents)
add_cache_metrics("agent_details", store_cache._get_cached_agent_details)
add_cache_metrics("store_creators", store_cache._get_cached_store_creators)
add_cache_metrics("creator_details", store_cache._get_cached_creator_details)
# Add metadata/help text at the beginning
prometheus_output = [

View File

@@ -4,18 +4,12 @@ Test suite for verifying cache_delete functionality in store routes.
Tests that specific cache entries can be deleted while preserving others.
"""
import datetime
from unittest.mock import AsyncMock, patch
import pytest
from backend.server.v2.store import routes
from backend.server.v2.store.model import (
ProfileDetails,
StoreAgent,
StoreAgentDetails,
StoreAgentsResponse,
)
from backend.server.v2.store import cache as store_cache
from backend.server.v2.store.model import StoreAgent, StoreAgentsResponse
from backend.util.models import Pagination
@@ -54,10 +48,10 @@ class TestCacheDeletion:
return_value=mock_response,
) as mock_db:
# Clear cache first
routes._get_cached_store_agents.cache_clear()
store_cache._get_cached_store_agents.cache_clear()
# First call - should hit database
result1 = await routes._get_cached_store_agents(
result1 = await store_cache._get_cached_store_agents(
featured=False,
creator=None,
sorted_by=None,
@@ -70,7 +64,7 @@ class TestCacheDeletion:
assert result1.agents[0].agent_name == "Test Agent"
# Second call with same params - should use cache
await routes._get_cached_store_agents(
await store_cache._get_cached_store_agents(
featured=False,
creator=None,
sorted_by=None,
@@ -82,7 +76,7 @@ class TestCacheDeletion:
assert mock_db.call_count == 1 # No additional DB call
# Third call with different params - should hit database
await routes._get_cached_store_agents(
await store_cache._get_cached_store_agents(
featured=True, # Different param
creator=None,
sorted_by=None,
@@ -94,7 +88,7 @@ class TestCacheDeletion:
assert mock_db.call_count == 2 # New DB call
# Delete specific cache entry
deleted = routes._get_cached_store_agents.cache_delete(
deleted = store_cache._get_cached_store_agents.cache_delete(
featured=False,
creator=None,
sorted_by=None,
@@ -106,7 +100,7 @@ class TestCacheDeletion:
assert deleted is True # Entry was deleted
# Try to delete non-existent entry
deleted = routes._get_cached_store_agents.cache_delete(
deleted = store_cache._get_cached_store_agents.cache_delete(
featured=False,
creator="nonexistent",
sorted_by=None,
@@ -118,7 +112,7 @@ class TestCacheDeletion:
assert deleted is False # Entry didn't exist
# Call with deleted params - should hit database again
await routes._get_cached_store_agents(
await store_cache._get_cached_store_agents(
featured=False,
creator=None,
sorted_by=None,
@@ -130,7 +124,7 @@ class TestCacheDeletion:
assert mock_db.call_count == 3 # New DB call after deletion
# Call with featured=True - should still be cached
await routes._get_cached_store_agents(
await store_cache._get_cached_store_agents(
featured=True,
creator=None,
sorted_by=None,
@@ -141,105 +135,11 @@ class TestCacheDeletion:
)
assert mock_db.call_count == 3 # No additional DB call
@pytest.mark.asyncio
async def test_agent_details_cache_delete(self):
"""Test that specific agent details cache entries can be deleted."""
mock_response = StoreAgentDetails(
store_listing_version_id="version1",
slug="test-agent",
agent_name="Test Agent",
agent_video="https://example.com/video.mp4",
agent_image=["https://example.com/image.jpg"],
creator="testuser",
creator_avatar="https://example.com/avatar.jpg",
sub_heading="Test subheading",
description="Test description",
categories=["productivity"],
runs=100,
rating=4.5,
versions=[],
last_updated=datetime.datetime(2024, 1, 1),
)
with patch(
"backend.server.v2.store.db.get_store_agent_details",
new_callable=AsyncMock,
return_value=mock_response,
) as mock_db:
# Clear cache first
routes._get_cached_agent_details.cache_clear()
# First call - should hit database
await routes._get_cached_agent_details(
username="testuser", agent_name="testagent"
)
assert mock_db.call_count == 1
# Second call - should use cache
await routes._get_cached_agent_details(
username="testuser", agent_name="testagent"
)
assert mock_db.call_count == 1 # No additional DB call
# Delete specific entry
deleted = routes._get_cached_agent_details.cache_delete(
username="testuser", agent_name="testagent"
)
assert deleted is True
# Call again - should hit database
await routes._get_cached_agent_details(
username="testuser", agent_name="testagent"
)
assert mock_db.call_count == 2 # New DB call after deletion
@pytest.mark.asyncio
async def test_user_profile_cache_delete(self):
"""Test that user profile cache entries can be deleted."""
mock_response = ProfileDetails(
name="Test User",
username="testuser",
description="Test profile",
links=["https://example.com"],
)
with patch(
"backend.server.v2.store.db.get_user_profile",
new_callable=AsyncMock,
return_value=mock_response,
) as mock_db:
# Clear cache first
routes._get_cached_user_profile.cache_clear()
# First call - should hit database
await routes._get_cached_user_profile("user123")
assert mock_db.call_count == 1
# Second call - should use cache
await routes._get_cached_user_profile("user123")
assert mock_db.call_count == 1
# Different user - should hit database
await routes._get_cached_user_profile("user456")
assert mock_db.call_count == 2
# Delete specific user's cache
deleted = routes._get_cached_user_profile.cache_delete("user123")
assert deleted is True
# user123 should hit database again
await routes._get_cached_user_profile("user123")
assert mock_db.call_count == 3
# user456 should still be cached
await routes._get_cached_user_profile("user456")
assert mock_db.call_count == 3 # No additional DB call
@pytest.mark.asyncio
async def test_cache_info_after_deletions(self):
"""Test that cache_info correctly reflects deletions."""
# Clear all caches first
routes._get_cached_store_agents.cache_clear()
store_cache._get_cached_store_agents.cache_clear()
mock_response = StoreAgentsResponse(
agents=[],
@@ -258,7 +158,7 @@ class TestCacheDeletion:
):
# Add multiple entries
for i in range(5):
await routes._get_cached_store_agents(
await store_cache._get_cached_store_agents(
featured=False,
creator=f"creator{i}",
sorted_by=None,
@@ -269,12 +169,12 @@ class TestCacheDeletion:
)
# Check cache size
info = routes._get_cached_store_agents.cache_info()
info = store_cache._get_cached_store_agents.cache_info()
assert info["size"] == 5
# Delete some entries
for i in range(2):
deleted = routes._get_cached_store_agents.cache_delete(
deleted = store_cache._get_cached_store_agents.cache_delete(
featured=False,
creator=f"creator{i}",
sorted_by=None,
@@ -286,7 +186,7 @@ class TestCacheDeletion:
assert deleted is True
# Check cache size after deletion
info = routes._get_cached_store_agents.cache_info()
info = store_cache._get_cached_store_agents.cache_info()
assert info["size"] == 3
@pytest.mark.asyncio
@@ -307,10 +207,10 @@ class TestCacheDeletion:
new_callable=AsyncMock,
return_value=mock_response,
) as mock_db:
routes._get_cached_store_agents.cache_clear()
store_cache._get_cached_store_agents.cache_clear()
# Test with all parameters
await routes._get_cached_store_agents(
await store_cache._get_cached_store_agents(
featured=True,
creator="testuser",
sorted_by="rating",
@@ -322,7 +222,7 @@ class TestCacheDeletion:
assert mock_db.call_count == 1
# Delete with exact same parameters
deleted = routes._get_cached_store_agents.cache_delete(
deleted = store_cache._get_cached_store_agents.cache_delete(
featured=True,
creator="testuser",
sorted_by="rating",
@@ -334,7 +234,7 @@ class TestCacheDeletion:
assert deleted is True
# Try to delete with slightly different parameters
deleted = routes._get_cached_store_agents.cache_delete(
deleted = store_cache._get_cached_store_agents.cache_delete(
featured=True,
creator="testuser",
sorted_by="rating",

View File

@@ -2553,7 +2553,7 @@
"get": {
"tags": ["v2", "store", "public"],
"summary": "List store agents",
"description": "Get a paginated list of agents from the store with optional filtering and sorting.\nResults are cached for 15 minutes.\n\nArgs:\n featured (bool, optional): Filter to only show featured agents. Defaults to False.\n creator (str | None, optional): Filter agents by creator username. Defaults to None.\n sorted_by (str | None, optional): Sort agents by \"runs\" or \"rating\". Defaults to None.\n search_query (str | None, optional): Search agents by name, subheading and description. Defaults to None.\n category (str | None, optional): Filter agents by category. Defaults to None.\n page (int, optional): Page number for pagination. Defaults to 1.\n page_size (int, optional): Number of agents per page. Defaults to 20.\n\nReturns:\n StoreAgentsResponse: Paginated list of agents matching the filters\n\nRaises:\n HTTPException: If page or page_size are less than 1\n\nUsed for:\n- Home Page Featured Agents\n- Home Page Top Agents\n- Search Results\n- Agent Details - Other Agents By Creator\n- Agent Details - Similar Agents\n- Creator Details - Agents By Creator",
"description": "Get a paginated list of agents from the store with optional filtering and sorting.\n\nArgs:\n featured (bool, optional): Filter to only show featured agents. Defaults to False.\n creator (str | None, optional): Filter agents by creator username. Defaults to None.\n sorted_by (str | None, optional): Sort agents by \"runs\" or \"rating\". Defaults to None.\n search_query (str | None, optional): Search agents by name, subheading and description. Defaults to None.\n category (str | None, optional): Filter agents by category. Defaults to None.\n page (int, optional): Page number for pagination. Defaults to 1.\n page_size (int, optional): Number of agents per page. Defaults to 20.\n\nReturns:\n StoreAgentsResponse: Paginated list of agents matching the filters\n\nRaises:\n HTTPException: If page or page_size are less than 1\n\nUsed for:\n- Home Page Featured Agents\n- Home Page Top Agents\n- Search Results\n- Agent Details - Other Agents By Creator\n- Agent Details - Similar Agents\n- Creator Details - Agents By Creator",
"operationId": "getV2List store agents",
"parameters": [
{
@@ -2639,7 +2639,7 @@
"get": {
"tags": ["v2", "store", "public"],
"summary": "Get specific agent",
"description": "This is only used on the AgentDetails Page.\nResults are cached for 15 minutes.\n\nIt returns the store listing agents details.",
"description": "This is only used on the AgentDetails Page.\n\nIt returns the store listing agents details.",
"operationId": "getV2Get specific agent",
"parameters": [
{
@@ -2679,7 +2679,7 @@
"get": {
"tags": ["v2", "store"],
"summary": "Get agent graph",
"description": "Get Agent Graph from Store Listing Version ID.\nResults are cached for 1 hour.",
"description": "Get Agent Graph from Store Listing Version ID.",
"operationId": "getV2Get agent graph",
"security": [{ "HTTPBearerJWT": [] }],
"parameters": [
@@ -2713,7 +2713,7 @@
"get": {
"tags": ["v2", "store"],
"summary": "Get agent by version",
"description": "Get Store Agent Details from Store Listing Version ID.\nResults are cached for 1 hour.",
"description": "Get Store Agent Details from Store Listing Version ID.",
"operationId": "getV2Get agent by version",
"security": [{ "HTTPBearerJWT": [] }],
"parameters": [
@@ -2803,7 +2803,7 @@
"get": {
"tags": ["v2", "store", "public"],
"summary": "List store creators",
"description": "This is needed for:\n- Home Page Featured Creators\n- Search Results Page\n\nResults are cached for 1 hour.\n\n---\n\nTo support this functionality we need:\n- featured: bool - to limit the list to just featured agents\n- search_query: str - vector search based on the creators profile description.\n- sorted_by: [agent_rating, agent_runs] -",
"description": "This is needed for:\n- Home Page Featured Creators\n- Search Results Page\n\n---\n\nTo support this functionality we need:\n- featured: bool - to limit the list to just featured agents\n- search_query: str - vector search based on the creators profile description.\n- sorted_by: [agent_rating, agent_runs] -",
"operationId": "getV2List store creators",
"parameters": [
{
@@ -2871,7 +2871,7 @@
"get": {
"tags": ["v2", "store", "public"],
"summary": "Get creator details",
"description": "Get the details of a creator.\nResults are cached for 1 hour.\n- Creator Details Page",
"description": "Get the details of a creator.\n- Creator Details Page",
"operationId": "getV2Get creator details",
"parameters": [
{
@@ -2905,7 +2905,7 @@
"get": {
"tags": ["v2", "store", "private"],
"summary": "Get my agents",
"description": "Get user's own agents.\nResults are cached for 5 minutes per user.",
"description": "Get user's own agents.",
"operationId": "getV2Get my agents",
"security": [{ "HTTPBearerJWT": [] }],
"parameters": [
@@ -3000,7 +3000,7 @@
"get": {
"tags": ["v2", "store", "private"],
"summary": "List my submissions",
"description": "Get a paginated list of store submissions for the authenticated user.\nResults are cached for 1 hour per user.\n\nArgs:\n user_id (str): ID of the authenticated user\n page (int, optional): Page number for pagination. Defaults to 1.\n page_size (int, optional): Number of submissions per page. Defaults to 20.\n\nReturns:\n StoreListingsResponse: Paginated list of store submissions\n\nRaises:\n HTTPException: If page or page_size are less than 1",
"description": "Get a paginated list of store submissions for the authenticated user.\n\nArgs:\n user_id (str): ID of the authenticated user\n page (int, optional): Page number for pagination. Defaults to 1.\n page_size (int, optional): Number of submissions per page. Defaults to 20.\n\nReturns:\n StoreListingsResponse: Paginated list of store submissions\n\nRaises:\n HTTPException: If page or page_size are less than 1",
"operationId": "getV2List my submissions",
"security": [{ "HTTPBearerJWT": [] }],
"parameters": [