activegraph 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- activegraph/__init__.py +222 -0
- activegraph/__main__.py +5 -0
- activegraph/behaviors/__init__.py +1 -0
- activegraph/behaviors/base.py +153 -0
- activegraph/behaviors/decorators.py +218 -0
- activegraph/cli/__init__.py +17 -0
- activegraph/cli/main.py +793 -0
- activegraph/cli/quickstart.py +556 -0
- activegraph/core/__init__.py +1 -0
- activegraph/core/clock.py +46 -0
- activegraph/core/event.py +33 -0
- activegraph/core/graph.py +846 -0
- activegraph/core/ids.py +135 -0
- activegraph/core/patch.py +47 -0
- activegraph/core/view.py +59 -0
- activegraph/errors.py +345 -0
- activegraph/frame.py +15 -0
- activegraph/llm/__init__.py +51 -0
- activegraph/llm/anthropic.py +339 -0
- activegraph/llm/cache.py +180 -0
- activegraph/llm/errors.py +257 -0
- activegraph/llm/prompt.py +420 -0
- activegraph/llm/provider.py +66 -0
- activegraph/llm/recorded.py +270 -0
- activegraph/llm/types.py +130 -0
- activegraph/observability/__init__.py +61 -0
- activegraph/observability/logging.py +205 -0
- activegraph/observability/metrics.py +219 -0
- activegraph/observability/migration.py +404 -0
- activegraph/observability/prometheus.py +101 -0
- activegraph/observability/status.py +83 -0
- activegraph/packs/__init__.py +999 -0
- activegraph/packs/diligence/__init__.py +86 -0
- activegraph/packs/diligence/behaviors.py +447 -0
- activegraph/packs/diligence/fixtures/__init__.py +364 -0
- activegraph/packs/diligence/fixtures/companies.py +511 -0
- activegraph/packs/diligence/object_types.py +163 -0
- activegraph/packs/diligence/prompts/document_researcher.md +25 -0
- activegraph/packs/diligence/prompts/memo_synthesizer.md +35 -0
- activegraph/packs/diligence/prompts/question_generator.md +25 -0
- activegraph/packs/diligence/prompts/risk_identifier.md +26 -0
- activegraph/packs/diligence/settings.py +60 -0
- activegraph/packs/diligence/tools.py +124 -0
- activegraph/packs/loader.py +709 -0
- activegraph/packs/scaffold.py +317 -0
- activegraph/policy.py +20 -0
- activegraph/runtime/__init__.py +1 -0
- activegraph/runtime/behavior_graph.py +151 -0
- activegraph/runtime/budget.py +120 -0
- activegraph/runtime/config_errors.py +124 -0
- activegraph/runtime/diff.py +145 -0
- activegraph/runtime/errors.py +216 -0
- activegraph/runtime/exec_errors.py +232 -0
- activegraph/runtime/patterns.py +946 -0
- activegraph/runtime/queue.py +27 -0
- activegraph/runtime/registration_errors.py +291 -0
- activegraph/runtime/registry.py +111 -0
- activegraph/runtime/runtime.py +2441 -0
- activegraph/runtime/scheduler.py +206 -0
- activegraph/runtime/view_builder.py +65 -0
- activegraph/store/__init__.py +41 -0
- activegraph/store/base.py +66 -0
- activegraph/store/conformance.py +161 -0
- activegraph/store/errors.py +84 -0
- activegraph/store/memory.py +118 -0
- activegraph/store/postgres.py +609 -0
- activegraph/store/serde.py +202 -0
- activegraph/store/sqlite.py +446 -0
- activegraph/store/url.py +230 -0
- activegraph/tools/__init__.py +64 -0
- activegraph/tools/base.py +57 -0
- activegraph/tools/cache.py +157 -0
- activegraph/tools/context.py +48 -0
- activegraph/tools/decorators.py +70 -0
- activegraph/tools/errors.py +320 -0
- activegraph/tools/graph_query.py +94 -0
- activegraph/tools/recorded.py +205 -0
- activegraph/tools/web_fetch.py +80 -0
- activegraph/trace/__init__.py +1 -0
- activegraph/trace/causal.py +123 -0
- activegraph/trace/printer.py +495 -0
- activegraph-1.0.0.dist-info/METADATA +282 -0
- activegraph-1.0.0.dist-info/RECORD +86 -0
- activegraph-1.0.0.dist-info/WHEEL +5 -0
- activegraph-1.0.0.dist-info/entry_points.txt +5 -0
- activegraph-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
version = "1.0.0"
|
|
3
|
+
---
|
|
4
|
+
You are a diligence researcher answering one question about a company.
|
|
5
|
+
|
|
6
|
+
Workflow:
|
|
7
|
+
1. Call `fetch_company_docs` with the company name to obtain a list of
|
|
8
|
+
relevant source documents.
|
|
9
|
+
2. Identify the most promising 1-2 documents for the question at hand.
|
|
10
|
+
3. Call `summarize_document` on each to extract its summary and key
|
|
11
|
+
facts.
|
|
12
|
+
4. From the extracted facts, formulate claims that answer the question.
|
|
13
|
+
For each claim:
|
|
14
|
+
- Write the claim in one sentence, verbatim where possible.
|
|
15
|
+
- Assign a confidence score (0.0-1.0) calibrated to evidence
|
|
16
|
+
strength. Below 0.5: rumor or weak inference. 0.5-0.7: plausible
|
|
17
|
+
but unverified. 0.7-0.9: well-supported by the document. 0.9+:
|
|
18
|
+
directly stated, primary source.
|
|
19
|
+
- Provide a verbatim evidence_quote — the exact sentence in the
|
|
20
|
+
document that supports the claim.
|
|
21
|
+
|
|
22
|
+
Do not invent claims. If a document does not address the question,
|
|
23
|
+
return an empty claims list and a summary that says so.
|
|
24
|
+
|
|
25
|
+
Return a single ResearchFindings object per the schema.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
version = "1.0.0"
|
|
3
|
+
---
|
|
4
|
+
You are synthesizing the final diligence memo for a company.
|
|
5
|
+
|
|
6
|
+
The memo MUST have exactly these sections, in order:
|
|
7
|
+
|
|
8
|
+
1. **summary**: 2-4 sentence executive summary. State the company,
|
|
9
|
+
the thesis question, and the bottom-line conclusion (or "open" if
|
|
10
|
+
unresolved).
|
|
11
|
+
|
|
12
|
+
2. **thesis_questions_addressed**: a list, one entry per research
|
|
13
|
+
question with shape `{question, status, claim_ids}`. `status` is
|
|
14
|
+
"answered" or "unresolved". `claim_ids` lists the ids of claims
|
|
15
|
+
that address the question.
|
|
16
|
+
|
|
17
|
+
3. **key_claims**: a list of the most material claims. Each entry has
|
|
18
|
+
shape `{claim_id, text, evidence_ids}`. Every claim entry MUST
|
|
19
|
+
include at least one evidence_id — uncited claims are not
|
|
20
|
+
permitted in the memo.
|
|
21
|
+
|
|
22
|
+
4. **open_contradictions**: a list of unresolved contradictions, each
|
|
23
|
+
with shape `{contradiction_id, claim_a_text, claim_b_text}`. If
|
|
24
|
+
the run produced no contradictions, leave this list empty AND set
|
|
25
|
+
`contradictions_note` to the string "no contradictions found".
|
|
26
|
+
|
|
27
|
+
5. **risks**: a list of the identified risks, each with shape
|
|
28
|
+
`{risk_id, title, severity, description}`. At least one risk MUST
|
|
29
|
+
be listed.
|
|
30
|
+
|
|
31
|
+
Read the graph view carefully — the data you need is all there. Do
|
|
32
|
+
not invent claims, evidence, contradictions, or risks. Use only
|
|
33
|
+
what the graph contains.
|
|
34
|
+
|
|
35
|
+
Return a single MemoBody object per the schema.
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
version = "1.0.0"
|
|
3
|
+
---
|
|
4
|
+
You are generating the initial research questions for an investment
|
|
5
|
+
diligence run on a single company.
|
|
6
|
+
|
|
7
|
+
Read the triggering event (a `company.created` event) and the view of
|
|
8
|
+
the graph (which contains the company object). Produce between
|
|
9
|
+
min_questions and max_questions distinct research questions that, if
|
|
10
|
+
answered, would constitute a thorough due-diligence picture of the
|
|
11
|
+
company.
|
|
12
|
+
|
|
13
|
+
Cover at least:
|
|
14
|
+
- Financial performance (revenue, margins, trajectory)
|
|
15
|
+
- Product (what they sell, how it's differentiated)
|
|
16
|
+
- Customers (segments, concentration, retention)
|
|
17
|
+
- Market (size, growth, competitive dynamics)
|
|
18
|
+
- Risks (regulatory, technological, executional)
|
|
19
|
+
|
|
20
|
+
Questions should be:
|
|
21
|
+
- Specific and answerable, not vague
|
|
22
|
+
- Each focused on one concern (no compound questions)
|
|
23
|
+
- Phrased so a researcher knows what document/source to consult
|
|
24
|
+
|
|
25
|
+
Return only the questions as a JSON list per the schema.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
version = "1.0.0"
|
|
3
|
+
---
|
|
4
|
+
You are identifying material risks for a company based on its
|
|
5
|
+
accumulated claims.
|
|
6
|
+
|
|
7
|
+
You receive the graph view containing the company, its claims, the
|
|
8
|
+
documents those claims were derived from, and any detected
|
|
9
|
+
contradictions. Read all of it before responding.
|
|
10
|
+
|
|
11
|
+
For each risk you identify:
|
|
12
|
+
- Title: short noun phrase (e.g. "customer concentration", "regulatory
|
|
13
|
+
exposure to FDA action").
|
|
14
|
+
- Description: 1-3 sentences explaining the risk in concrete terms,
|
|
15
|
+
grounded in the observed claims.
|
|
16
|
+
- Severity: "low", "medium", or "high".
|
|
17
|
+
- related_claim_texts: list the verbatim text of the claims that
|
|
18
|
+
contribute to this risk. The handler will resolve these back to
|
|
19
|
+
claim ids; exact text match required.
|
|
20
|
+
|
|
21
|
+
Identify at least one risk per company. If a contradiction was
|
|
22
|
+
detected, it is itself a risk (severity at least "medium") and must
|
|
23
|
+
appear in your output. Material risks only — do not list every
|
|
24
|
+
hypothetical concern.
|
|
25
|
+
|
|
26
|
+
Return a RiskList object per the schema.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Diligence pack settings. CONTRACT v0.9 #15.
|
|
2
|
+
|
|
3
|
+
Every field has a default so `runtime.load_pack(diligence_pack)`
|
|
4
|
+
without `settings=` works for the quickstart. The killer demo
|
|
5
|
+
overrides explicitly to demonstrate the typed-injection API.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, Field
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DiligenceSettings(BaseModel):
|
|
14
|
+
"""Configuration for the Diligence pack.
|
|
15
|
+
|
|
16
|
+
Accessed by behaviors in three forms (CONTRACT v0.9 #7):
|
|
17
|
+
1. Typed parameter injection (primary):
|
|
18
|
+
`def claim_extractor(event, graph, ctx, out, *,
|
|
19
|
+
settings: DiligenceSettings): ...`
|
|
20
|
+
2. `ctx.settings.confidence_threshold_for_review`
|
|
21
|
+
3. `ctx.pack_settings("diligence")` for cross-pack lookups.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
llm_model: str = Field(
|
|
25
|
+
default="claude-sonnet-4-5",
|
|
26
|
+
description="The Claude model used by all diligence LLM behaviors.",
|
|
27
|
+
)
|
|
28
|
+
max_documents_per_company: int = Field(
|
|
29
|
+
default=5, ge=1,
|
|
30
|
+
description="Cap on documents fetched per company. Bounds tool cost.",
|
|
31
|
+
)
|
|
32
|
+
max_claims_per_document: int = Field(
|
|
33
|
+
default=20, ge=1,
|
|
34
|
+
description="Cap on claims extracted per document. Bounds LLM cost and "
|
|
35
|
+
"downstream evidence-linking work.",
|
|
36
|
+
)
|
|
37
|
+
confidence_threshold_for_review: float = Field(
|
|
38
|
+
default=0.7, ge=0.0, le=1.0,
|
|
39
|
+
description="Claims with confidence ≥ this threshold participate in "
|
|
40
|
+
"contradiction detection.",
|
|
41
|
+
)
|
|
42
|
+
min_questions: int = Field(
|
|
43
|
+
default=8, ge=1,
|
|
44
|
+
description="Minimum number of thesis questions the generator produces.",
|
|
45
|
+
)
|
|
46
|
+
max_questions: int = Field(
|
|
47
|
+
default=15, ge=1,
|
|
48
|
+
description="Maximum number of thesis questions. Generator is one-shot "
|
|
49
|
+
"in v0.9 (CONTRACT v0.9 #16); adaptive in v1.0.",
|
|
50
|
+
)
|
|
51
|
+
auto_approve_memos: bool = Field(
|
|
52
|
+
default=True,
|
|
53
|
+
description="When False, memo writes go through the memo_approval "
|
|
54
|
+
"policy (CONTRACT v0.9 #15 / #9). True for quickstart.",
|
|
55
|
+
)
|
|
56
|
+
auto_approve_risks: bool = Field(
|
|
57
|
+
default=True,
|
|
58
|
+
description="When False, risk writes go through the risk_approval "
|
|
59
|
+
"policy. True for quickstart.",
|
|
60
|
+
)
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"""Diligence pack tools. CONTRACT v0.9 #15.
|
|
2
|
+
|
|
3
|
+
Three pack-scoped tools backed by recorded fixtures. Production users
|
|
4
|
+
swap these for real implementations:
|
|
5
|
+
- fetch_company_docs: would call a real fetcher (SEC, news APIs, etc.)
|
|
6
|
+
- search_filings: would query EDGAR or a similar service
|
|
7
|
+
- summarize_document: an LLM-backed summarizer with caching
|
|
8
|
+
|
|
9
|
+
The fixtures live in `activegraph.packs.diligence.fixtures` per
|
|
10
|
+
CONTRACT v0.9 #18 (fixtures ship with the pack).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from decimal import Decimal
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
|
|
20
|
+
from activegraph.packs import tool
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------------------------------------------------- I/O schemas
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FetchCompanyDocsInput(BaseModel):
|
|
27
|
+
company_name: str
|
|
28
|
+
max_results: int = 5
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class DocumentRef(BaseModel):
|
|
32
|
+
title: str
|
|
33
|
+
url: str
|
|
34
|
+
summary: str = ""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FetchCompanyDocsOutput(BaseModel):
|
|
38
|
+
documents: list[DocumentRef]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SearchFilingsInput(BaseModel):
|
|
42
|
+
company_name: str
|
|
43
|
+
keyword: str = ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class SearchFilingsOutput(BaseModel):
|
|
47
|
+
filings: list[DocumentRef]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SummarizeDocumentInput(BaseModel):
|
|
51
|
+
url: str
|
|
52
|
+
max_words: int = 100
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class SummarizeDocumentOutput(BaseModel):
|
|
56
|
+
summary: str
|
|
57
|
+
extracted_facts: list[str] = []
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# ---------------------------------------------------- tool registration
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@tool(
|
|
64
|
+
name="fetch_company_docs",
|
|
65
|
+
description="Fetch a list of source documents for a company. "
|
|
66
|
+
"Returns up to max_results documents with title, URL, "
|
|
67
|
+
"and a one-line summary.",
|
|
68
|
+
input_schema=FetchCompanyDocsInput,
|
|
69
|
+
output_schema=FetchCompanyDocsOutput,
|
|
70
|
+
cost_per_call=Decimal("0.005"),
|
|
71
|
+
timeout_seconds=15.0,
|
|
72
|
+
deterministic=False,
|
|
73
|
+
)
|
|
74
|
+
def fetch_company_docs(args: FetchCompanyDocsInput, ctx) -> FetchCompanyDocsOutput:
|
|
75
|
+
"""Backed by fixtures in v0.9. Production: real fetcher."""
|
|
76
|
+
from activegraph.packs.diligence.fixtures import lookup_company_docs
|
|
77
|
+
|
|
78
|
+
docs = lookup_company_docs(args.company_name, max_results=args.max_results)
|
|
79
|
+
return FetchCompanyDocsOutput(
|
|
80
|
+
documents=[DocumentRef(**d) for d in docs],
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@tool(
|
|
85
|
+
name="search_filings",
|
|
86
|
+
description="Search a company's regulatory filings by keyword. "
|
|
87
|
+
"Returns matching filings as document refs.",
|
|
88
|
+
input_schema=SearchFilingsInput,
|
|
89
|
+
output_schema=SearchFilingsOutput,
|
|
90
|
+
cost_per_call=Decimal("0.003"),
|
|
91
|
+
timeout_seconds=15.0,
|
|
92
|
+
deterministic=False,
|
|
93
|
+
)
|
|
94
|
+
def search_filings(args: SearchFilingsInput, ctx) -> SearchFilingsOutput:
|
|
95
|
+
"""Backed by fixtures in v0.9. Production: EDGAR query."""
|
|
96
|
+
from activegraph.packs.diligence.fixtures import lookup_filings
|
|
97
|
+
|
|
98
|
+
filings = lookup_filings(args.company_name, args.keyword)
|
|
99
|
+
return SearchFilingsOutput(
|
|
100
|
+
filings=[DocumentRef(**f) for f in filings],
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@tool(
|
|
105
|
+
name="summarize_document",
|
|
106
|
+
description="Summarize a document at a given URL and extract key facts.",
|
|
107
|
+
input_schema=SummarizeDocumentInput,
|
|
108
|
+
output_schema=SummarizeDocumentOutput,
|
|
109
|
+
cost_per_call=Decimal("0.002"),
|
|
110
|
+
timeout_seconds=30.0,
|
|
111
|
+
deterministic=False,
|
|
112
|
+
)
|
|
113
|
+
def summarize_document(args: SummarizeDocumentInput, ctx) -> SummarizeDocumentOutput:
|
|
114
|
+
"""Backed by fixtures in v0.9. Production: LLM summarizer."""
|
|
115
|
+
from activegraph.packs.diligence.fixtures import lookup_summary
|
|
116
|
+
|
|
117
|
+
result = lookup_summary(args.url)
|
|
118
|
+
return SummarizeDocumentOutput(
|
|
119
|
+
summary=result.get("summary", "")[: args.max_words * 8], # rough char cap
|
|
120
|
+
extracted_facts=result.get("facts", []),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
TOOLS = [fetch_company_docs, search_filings, summarize_document]
|