FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
94 lines
2.7 KiB
Python
94 lines
2.7 KiB
Python
"""Company-enrichment provider interface. Answers "what does a paid,
|
|
first-party data vendor already know about this company?" - a richer,
|
|
metered complement to the free SearchProvider/LLM discovery pipeline
|
|
(app/services/discovery_service.py), never a replacement for it. Every
|
|
method here maps to one NinjaPear (nubela.co) API endpoint; orchestration
|
|
(call order, the leadership-lookup cap, partial-failure handling) lives in
|
|
app/services/enrichment_service.py, not here - this Protocol is a dumb I/O
|
|
layer, matching app/search/base.py's shape.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Protocol
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class LeadershipMember(BaseModel):
|
|
name: str
|
|
title: str | None = None
|
|
work_email: str | None = None
|
|
profile_url: str | None = None
|
|
bio: str | None = None
|
|
|
|
|
|
class CompanyDetails(BaseModel):
|
|
description: str | None = None
|
|
industry: str | None = None
|
|
founded_year: int | None = None
|
|
specialties: list[str] = Field(default_factory=list)
|
|
leadership_team: list[LeadershipMember] = Field(default_factory=list)
|
|
employee_count_range: str | None = None
|
|
|
|
|
|
class FundingRound(BaseModel):
|
|
round_name: str | None = None
|
|
amount: str | None = None
|
|
date: str | None = None
|
|
investors: list[str] = Field(default_factory=list)
|
|
|
|
|
|
class CompanyFunding(BaseModel):
|
|
total_raised: str | None = None
|
|
rounds: list[FundingRound] = Field(default_factory=list)
|
|
|
|
|
|
class CompetitorWithReason(BaseModel):
|
|
name: str
|
|
reason: str | None = None
|
|
|
|
|
|
class Product(BaseModel):
|
|
name: str
|
|
description: str | None = None
|
|
category: str | None = None
|
|
|
|
|
|
class RecentUpdate(BaseModel):
|
|
type: str
|
|
text: str
|
|
url: str | None = None
|
|
date: str | None = None
|
|
|
|
|
|
class Customer(BaseModel):
|
|
name: str
|
|
relationship: str | None = None
|
|
|
|
|
|
class EnrichmentProvider(Protocol):
|
|
provider_name: str
|
|
|
|
async def get_company_details(self, name: str, website: str | None) -> CompanyDetails: ...
|
|
|
|
async def get_funding(self, name: str, website: str | None) -> CompanyFunding: ...
|
|
|
|
async def get_updates(self, name: str, website: str | None) -> list[RecentUpdate]: ...
|
|
|
|
async def get_competitors(
|
|
self, name: str, website: str | None
|
|
) -> list[CompetitorWithReason]: ...
|
|
|
|
async def get_products(self, name: str, website: str | None) -> list[Product]: ...
|
|
|
|
async def get_customers(self, name: str, website: str | None) -> list[Customer]: ...
|
|
|
|
async def get_work_email(self, person_name: str, company_website: str) -> str | None: ...
|
|
|
|
async def get_person_profile(
|
|
self, person_name: str, company_website: str
|
|
) -> tuple[str | None, str | None]:
|
|
"""Returns (profile_url, bio)."""
|
|
...
|