mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-10 22:32:16 +02:00
feat: docs and ui tweaks
This commit is contained in:
parent
64f2b4a6eb
commit
271a21aee6
103 changed files with 2161 additions and 2625 deletions
|
|
@ -28,7 +28,11 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||
|
||||
from app.auth.context import AuthContext
|
||||
from app.capabilities.core.access.rate_limit import enforce_capability_rate_limit
|
||||
from app.capabilities.core.billing import charge_capability, gate_capability
|
||||
from app.capabilities.core.billing import (
|
||||
charge_capability,
|
||||
gate_capability,
|
||||
pricing_meters,
|
||||
)
|
||||
from app.capabilities.core.events import run_event_bus
|
||||
from app.capabilities.core.progress import progress_scope
|
||||
from app.capabilities.core.runs import (
|
||||
|
|
@ -55,13 +59,22 @@ _SSE_HEADERS = {
|
|||
}
|
||||
|
||||
|
||||
class PricingMeter(BaseModel):
|
||||
"""One live per-item rate a verb charges on, e.g. 3500 micro-USD per place."""
|
||||
|
||||
unit: str
|
||||
micros_per_unit: int
|
||||
|
||||
|
||||
class CapabilitySummary(BaseModel):
|
||||
"""A verb's identity + input/output JSON schemas, for the playground UI."""
|
||||
"""A verb's identity + input/output JSON schemas + pricing, for the playground UI."""
|
||||
|
||||
name: str
|
||||
description: str
|
||||
input_schema: dict
|
||||
output_schema: dict
|
||||
# Empty list = free (billing disabled or an unmetered verb).
|
||||
pricing: list[PricingMeter] = []
|
||||
|
||||
|
||||
class RunSummary(BaseModel):
|
||||
|
|
@ -125,12 +138,17 @@ def _register_capabilities_list(
|
|||
) -> None:
|
||||
"""Register the ``GET`` that lists verbs + their input schemas for the UI."""
|
||||
|
||||
summaries = [
|
||||
CapabilitySummary(
|
||||
name=capability.name,
|
||||
description=capability.description,
|
||||
input_schema=capability.input_schema.model_json_schema(),
|
||||
output_schema=capability.output_schema.model_json_schema(),
|
||||
# Schemas are static; pricing is attached per request because rates are
|
||||
# read live from config (env retune + restart, no rebuild).
|
||||
base_summaries = [
|
||||
(
|
||||
CapabilitySummary(
|
||||
name=capability.name,
|
||||
description=capability.description,
|
||||
input_schema=capability.input_schema.model_json_schema(),
|
||||
output_schema=capability.output_schema.model_json_schema(),
|
||||
),
|
||||
capability.billing_unit,
|
||||
)
|
||||
for capability in capabilities
|
||||
]
|
||||
|
|
@ -141,7 +159,12 @@ def _register_capabilities_list(
|
|||
auth: AuthContext = Depends(get_auth_context),
|
||||
) -> list[CapabilitySummary]:
|
||||
await check_workspace_access(session, auth, workspace_id)
|
||||
return summaries
|
||||
return [
|
||||
summary.model_copy(
|
||||
update={"pricing": [PricingMeter(**m) for m in pricing_meters(unit)]}
|
||||
)
|
||||
for summary, unit in base_summaries
|
||||
]
|
||||
|
||||
router.add_api_route(
|
||||
"/workspaces/{workspace_id}/scrapers/capabilities",
|
||||
|
|
|
|||
|
|
@ -43,6 +43,53 @@ def _platform_rate(unit: BillingUnit) -> int:
|
|||
return int(getattr(config, _PLATFORM_RATE_KEYS[unit]))
|
||||
|
||||
|
||||
# Display noun for each platform meter, e.g. "$3.50 / 1k places".
|
||||
_UNIT_NOUNS: dict[BillingUnit, str] = {
|
||||
BillingUnit.REDDIT_ITEM: "item",
|
||||
BillingUnit.GOOGLE_SEARCH_SERP: "SERP",
|
||||
BillingUnit.GOOGLE_MAPS_PLACE: "place",
|
||||
BillingUnit.GOOGLE_MAPS_REVIEW: "review",
|
||||
BillingUnit.YOUTUBE_VIDEO: "video",
|
||||
BillingUnit.YOUTUBE_COMMENT: "comment",
|
||||
}
|
||||
|
||||
|
||||
def pricing_meters(unit: BillingUnit | None) -> list[dict]:
|
||||
"""The live per-item rates a verb charges, for UI display. Empty = free.
|
||||
|
||||
Mirrors the gate/charge logic exactly: meters whose billing flag is off are
|
||||
omitted, so a self-hosted install with billing disabled reads as free.
|
||||
"""
|
||||
if unit is None:
|
||||
return []
|
||||
if unit is BillingUnit.WEB_CRAWL:
|
||||
meters = []
|
||||
if WebCrawlCreditService.billing_enabled():
|
||||
meters.append(
|
||||
{"unit": "page", "micros_per_unit": config.WEB_CRAWL_MICROS_PER_SUCCESS}
|
||||
)
|
||||
if WebCrawlCreditService.captcha_billing_enabled() and captcha_enabled():
|
||||
meters.append(
|
||||
{
|
||||
"unit": "captcha solve",
|
||||
"micros_per_unit": config.WEB_CRAWL_CAPTCHA_MICROS_PER_SOLVE,
|
||||
}
|
||||
)
|
||||
return meters
|
||||
if not config.PLATFORM_SCRAPE_BILLING_ENABLED:
|
||||
return []
|
||||
meters = [{"unit": _UNIT_NOUNS[unit], "micros_per_unit": _platform_rate(unit)}]
|
||||
if unit is BillingUnit.GOOGLE_MAPS_PLACE:
|
||||
# Dual-metered: attached reviews bill on their own meter.
|
||||
meters.append(
|
||||
{
|
||||
"unit": _UNIT_NOUNS[BillingUnit.GOOGLE_MAPS_REVIEW],
|
||||
"micros_per_unit": _platform_rate(BillingUnit.GOOGLE_MAPS_REVIEW),
|
||||
}
|
||||
)
|
||||
return meters
|
||||
|
||||
|
||||
async def gate_capability(
|
||||
payload: BillableInput, unit: BillingUnit | None, ctx: CapabilityContext
|
||||
) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue