178 lines
10 KiB
Python
178 lines
10 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import threading
|
|
import time
|
|
from io import StringIO
|
|
|
|
from django.core.management import call_command
|
|
|
|
from agents.venture_discovery import EVIDENCE_CEILINGS, SCORE_DEFINITIONS, SCORE_DIMENSIONS, VentureDiscoveryService
|
|
from control_plane.ventures.models import CompanyProposal, EvidenceTier, OverlapClassification, PortfolioICReview, VentureCapabilityDemand, VentureCohort, VentureCollision, VentureThesisFingerprint
|
|
from graph.bootstrap import champion_venture_discovery_cohort_graph_v1
|
|
from graph.langgraph_runtime import LangGraphRuntime
|
|
from graph.models import GraphRun, GraphRunStatus
|
|
from graph.venture_cohort import venture_discovery_cohort_registry
|
|
from model_router.router import ModelProvider, ModelRequestContract, ModelResponseContract, ModelRouter
|
|
|
|
|
|
class SequenceProvider(ModelProvider):
|
|
provider_name = "sol-sequence"
|
|
|
|
def __init__(self) -> None:
|
|
self.company_calls = 0
|
|
self.research_calls = 0
|
|
|
|
def complete(self, request: ModelRequestContract) -> ModelResponseContract:
|
|
if "Bounded public web market research" in request.prompt:
|
|
self.research_calls += 1
|
|
categories = ["competitors", "pricing", "customer_pain", "market_alternatives", "regulatory_platform_risks"]
|
|
return ModelResponseContract("luna", json.dumps({"sources": [{"url": f"https://example.com/{self.research_calls}/{cat}", "title": cat, "category": cat, "summary": cat} for cat in categories], "findings": {cat: [cat] for cat in categories}, "coverage": {cat: True for cat in categories}}), {})
|
|
self.company_calls += 1
|
|
n = self.company_calls
|
|
industries = ["Shopify", "Developer", "Legal", "Healthcare", "Real Estate", "Restaurant", "Security", "Education", "Logistics", "Finance"]
|
|
industry = industries[(n - 1) % len(industries)]
|
|
return ModelResponseContract("sol", json.dumps({"title": f"{industry} Validation Offer {n}", "one_line_thesis": f"Sell a productized {industry.lower()} audit to a narrow buyer before building software.", "description": f"A fixed-scope {industry} audit.", "problem": f"{industry} buyers have urgent operational gaps.", "target_customer": f"Small {industry} operators with active revenue.", "proposed_solution": f"Manual {industry} audit with prioritized fixes.", "business_model": "Productized service", "pricing_hypothesis": "$99 audit", "acquisition_strategy": "Compliant community posts and direct referrals after approval", "validation_plan": "Collect 5 credible target-customer responses or 1 willingness-to-pay signal before build/spend.", "capital_requested": "50", "time_to_first_dollar_estimate": "3-7 days after outreach approval", "expected_margin": "80-90% gross margin", "build_complexity": "LOW", "market_evidence": [], "differentiation": "Fast narrow audit", "major_risks": ["Demand unproven"], "confidence": 0.65}), {})
|
|
|
|
def health(self) -> str:
|
|
return "AVAILABLE"
|
|
|
|
|
|
def service() -> VentureDiscoveryService:
|
|
provider = SequenceProvider()
|
|
return VentureDiscoveryService(ModelRouter({"sol": provider, "luna": provider}), web_research_available=True)
|
|
|
|
|
|
def test_bounded_map_runs_concurrently_and_preserves_result_order() -> None:
|
|
svc = VentureDiscoveryService()
|
|
svc._effective_concurrency = lambda concurrency: concurrency
|
|
active = 0
|
|
observed_peak = 0
|
|
lock = threading.Lock()
|
|
|
|
def worker(value: int) -> int:
|
|
nonlocal active, observed_peak
|
|
with lock:
|
|
active += 1
|
|
observed_peak = max(observed_peak, active)
|
|
time.sleep(0.02)
|
|
with lock:
|
|
active -= 1
|
|
return value * 2
|
|
|
|
assert svc._bounded_map([1, 2, 3, 4], worker, 2) == [2, 4, 6, 8]
|
|
assert observed_peak == 2
|
|
assert svc._last_bounded_map_peak == 2
|
|
|
|
|
|
def test_evidence_tiers_probability_ceiling_and_score_orientation() -> None:
|
|
svc = service()
|
|
proposal = svc.generate_single_company(svc.create_v0_mandate())
|
|
assert proposal.evidence_tier == EvidenceTier.TIER_0_THESIS
|
|
assert svc.calibrate_probability(proposal, raw_probability=80)["evidence_adjusted_probability"] == EVIDENCE_CEILINGS[EvidenceTier.TIER_0_THESIS]
|
|
svc.conduct_market_research(proposal)
|
|
assert proposal.evidence_tier == EvidenceTier.TIER_1_PUBLIC_EVIDENCE
|
|
assert svc.calibrate_probability(proposal, raw_probability=80)["evidence_adjusted_probability"] == EVIDENCE_CEILINGS[EvidenceTier.TIER_1_PUBLIC_EVIDENCE]
|
|
diligence = svc.start_ic_diligence(proposal)
|
|
svc.generate_ic_questions(diligence)
|
|
svc.answer_questions(diligence)
|
|
svc.red_team(diligence)
|
|
svc.final_company_response(diligence)
|
|
decision = svc.score_and_decide(diligence)
|
|
assert set(decision.component_scores) == set(SCORE_DIMENSIONS)
|
|
assert "AI Leverage" in decision.component_scores
|
|
assert "Platformization Potential" in decision.component_scores
|
|
assert all("100 = highly attractive" in definition for definition in decision.score_definitions.values())
|
|
assert decision.probability_500_within_30_days <= decision.evidence_ceiling
|
|
assert decision.raw_probability_500_within_30_days >= decision.probability_500_within_30_days
|
|
|
|
|
|
def test_ai_native_platform_opportunities_receive_portfolio_preference() -> None:
|
|
svc = service()
|
|
mandate = svc.create_v0_mandate()
|
|
ai = svc.generate_single_company(mandate)
|
|
ai.title = "AI Support Knowledge Copilot"
|
|
ai.description = "AI agent platform that monitors support tickets, synthesizes answers, and builds a reusable knowledge workflow."
|
|
ai.proposed_solution = "Agent-assisted operational software with recurring automation and data accumulation."
|
|
ai.business_model = "Monthly SaaS platform plus AI-enabled service onboarding."
|
|
ai.differentiation = "Local inference and agent workflows automate most delivery with low marginal labor."
|
|
ai.save(update_fields=["title", "description", "proposed_solution", "business_model", "differentiation", "updated_at"])
|
|
generic = svc.generate_single_company(mandate)
|
|
generic.title = "Emergency Shopify Audit Fix"
|
|
generic.description = "One-time manual Shopify audit and emergency fix service."
|
|
generic.proposed_solution = "Manual one-time consulting audit."
|
|
generic.business_model = "One-time consulting service."
|
|
generic.differentiation = "Fast human review."
|
|
generic.save(update_fields=["title", "description", "proposed_solution", "business_model", "differentiation", "updated_at"])
|
|
|
|
assert svc.ai_leverage_score(ai) > svc.ai_leverage_score(generic)
|
|
assert svc.platformization_potential(ai) > svc.platformization_potential(generic)
|
|
assert svc._generic_concentration_penalty(generic) > svc._generic_concentration_penalty(ai)
|
|
|
|
|
|
def test_identity_contamination_detection_and_fingerprint_collision() -> None:
|
|
svc = service()
|
|
proposal = svc.generate_single_company(svc.create_v0_mandate())
|
|
contaminated = {"Company": "Unrelated CRM SaaS", "ICP": "enterprise banks", "Problem": "loan defaults", "Product": "risk platform"}
|
|
result = svc.validate_identity_content(proposal, contaminated)
|
|
assert result["passed"] is False
|
|
fp = svc.fingerprint_proposal(proposal)
|
|
assert isinstance(fp, VentureThesisFingerprint)
|
|
other = svc.generate_single_company(proposal.mandate)
|
|
other.title = proposal.title + " Copy"
|
|
other.target_customer = proposal.target_customer
|
|
other.problem = proposal.problem
|
|
other.proposed_solution = proposal.proposed_solution
|
|
other.business_model = proposal.business_model
|
|
other.save(update_fields=["title", "target_customer", "problem", "proposed_solution", "business_model", "updated_at"])
|
|
overlap = svc.classify_overlap(proposal, other)
|
|
assert overlap["classification"] in {OverlapClassification.NEAR_DUPLICATE, OverlapClassification.DUPLICATE, OverlapClassification.COMPETITIVE}
|
|
|
|
|
|
def test_cohort_size_ranking_top3_capability_aggregation_and_graph_lineage() -> None:
|
|
svc = service()
|
|
version = champion_venture_discovery_cohort_graph_v1()
|
|
graph_run = GraphRun.objects.create(execution_graph_version=version, current_node=version.graph_spec["entry"])
|
|
LangGraphRuntime(venture_discovery_cohort_registry(svc, cohort_size=10, concurrency=2)).run_until_terminal_or_paused(graph_run)
|
|
graph_run.refresh_from_db()
|
|
cohort = VentureCohort.objects.get(id=graph_run.metadata["cohort_id"])
|
|
|
|
assert graph_run.status == GraphRunStatus.COMPLETE
|
|
assert cohort.members.count() == 10
|
|
assert CompanyProposal.objects.count() == 10
|
|
assert cohort.members.filter(is_top_3=True).count() == 3
|
|
assert PortfolioICReview.objects.filter(cohort=cohort).exists()
|
|
assert VentureCollision.objects.filter(cohort=cohort).count() == 45
|
|
assert VentureCapabilityDemand.objects.filter(cohort=cohort).exists()
|
|
assert cohort.metadata["real_spend"] == 0
|
|
assert cohort.metadata["real_customer_outreach"] is False
|
|
assert cohort.concurrency == 2
|
|
assert cohort.metrics["peak_concurrency"] == 1
|
|
assert cohort.metrics["proposal_generation_peak_concurrency"] == 1
|
|
assert cohort.metrics["research_peak_concurrency"] == 1
|
|
assert cohort.metrics["individual_diligence_peak_concurrency"] == 1
|
|
report = cohort.mandate.artifacts.get(artifact_type="VENTURE_DISCOVERY_COHORT_REPORT")
|
|
assert len(report.content["rankings"]) == 10
|
|
assert len(report.content["top_3"]) == 3
|
|
assert report.content["total_spend"] == 0
|
|
|
|
|
|
def test_run_venture_cohort_management_command_outputs_summary(monkeypatch) -> None:
|
|
import control_plane.ventures.management.commands.run_venture_cohort as command_module
|
|
|
|
provider = SequenceProvider()
|
|
monkeypatch.setattr(command_module, "providers_from_resources", lambda: {"qwen": provider})
|
|
stdout = StringIO()
|
|
|
|
call_command("run_venture_cohort", "--size", "3", "--concurrency", "2", "--qwen-only", "--no-web-research", stdout=stdout)
|
|
|
|
summary = json.loads(stdout.getvalue())
|
|
cohort = VentureCohort.objects.get(cohort_id=summary["cohort_id"])
|
|
|
|
assert summary["status"] == GraphRunStatus.COMPLETE
|
|
assert summary["members"] == 3
|
|
assert summary["concurrency"] == 2
|
|
assert summary["fallback_count"] == 0
|
|
assert summary["generation_sources"] == ["qwen"]
|
|
assert len(summary["top_3"]) == 3
|
|
assert cohort.members.count() == 3
|