Imported from kody-w/rapp-monorepo (
repos/RAR/scout/workflows/rar-rarbookworld-momentfactory/skills/rar-rarbookworld-momentfactory/SKILL.md). Install upstream withnpx skills add kody-w/rapp-monorepo --skill rar-rarbookworld-momentfactory. Copyright stays with the author.
Microsoft Scout runtime
This is the reversible Scout projection of @rarbookworld/momentfactory. The original RAPP
agent is preserved byte-for-byte in momentfactory_agent.py and in the RCI capsule.
When Scout can execute local files, resolve this skill directory and run:
python3 scripts/run_agent.py --preflight
echo '{}' | python3 scripts/run_agent.py
Pass the real JSON arguments instead of {}. The runner verifies the linked
agent SHA-256 before importing it. If preflight reports a host dependency that
Scout cannot satisfy, use the brainstem_chat MCP tool to run the canonical
agent in the user's Brainstem. Never paraphrase the factory or agent into a new
implementation. The generic direct-file commands in the generated Toaster
section are recovery guidance; Scout should prefer the verified runner.
momentfactory_agent.py — the deployable MomentFactory singleton.
ONE sacred agent.py file containing the entire converged moment-to-Drop pipeline. Drop it into any RAPP brainstem's agents/ directory and it works.
This file is generated by tools/build-momentfactory.py from the multi-file source under agents/. The multi-file form is editable and iterable (the double-jump loop runs against it). This singleton is the SHIP-TIME artifact: no sibling-import dependencies, no helper modules, no repo layout assumptions. Just BasicAgent + an LLM key in the environment, and the public MomentFactory.perform(source, source_type, ...) → Drop.
Inlined personas (sacred SOULs preserved verbatim):
- Sensorium (normalize raw moment)
- SignificanceFilter (refuse low-significance moments — surprise specialist)
- HookWriter (1 sentence)
- BodyWriter (3-5 sentences)
- ChannelRouter (pick a Subrappter)
- CardForger (mint a RAR-compatible card)
- SeedStamper (deterministic 64-bit seed + 7-word incantation; NO LLM)
Public entrypoint: the MomentFactory class. Every internal class is prefixed with _Internal so the brainstem's *Agent discovery exposes only MomentFactory (via the MomentFactoryAgent alias at the bottom).
Generated from:
- agents/sensorium_agent.py
- agents/significance_filter_agent.py
- agents/hook_writer_agent.py
- agents/body_writer_agent.py
- agents/channel_router_agent.py
- agents/card_forger_agent.py
- agents/seed_stamper_agent.py
- agents/moment_factory_agent.py
Parameters
The typed contract this capability answers to (JSON Schema — the deterministic layer):
{
"properties": {
"significance_threshold": {
"description": "0..1 cutoff. Default 0.5.",
"type": "number"
},
"source": {
"description": "Raw moment text",
"type": "string"
},
"source_type": {
"description": "code-commit | voice-memo | web-bookmark | agent-run | location | conversation | decision | reading-note",
"type": "string"
}
},
"required": [
"source"
],
"type": "object"
}
Run this — do not improvise
This capability's deterministic implementation is a RAPP single-file agent, linked beside this file as momentfactory_agent.py and embedded as the fenced Python below (sha256 08cddf370c9a202c…; a byte-exact copy is also vaulted in the capsule comment at the end of this file). On a host with sandbox execution, run the linked file directly — if it is missing, write the fence contents verbatim to momentfactory_agent.py first:
python3 momentfactory_agent.py '{"key": "value"}' # arguments as one JSON object
echo '{"key": "value"}' | python3 momentfactory_agent.py # or on stdin
python3 momentfactory_agent.py --tool # emit the JSON tool contract
Treat stdout as a tool result. If it reports missing or unresolved inputs, stop and collect them. If it returns steps, execute those steps in order exactly as returned; if it returns instructions, follow them with the supplied inputs. Otherwise use the result verbatim. Do not invent behavior beyond that output. On a host without code execution, treat the Parameters schema and the code below as the exact specification and never paraphrase a step. Never edit inside the generated markers; a converter-equipped host can instead restore the original file checksum-verified with the installed rapp-agent-converter/scripts/toast.py convert SKILL.md --to agent.
"""
momentfactory_agent.py — the deployable MomentFactory singleton.
ONE sacred agent.py file containing the entire converged moment-to-Drop
pipeline. Drop it into any RAPP brainstem's agents/ directory and it works.
This file is generated by tools/build-momentfactory.py from the multi-file
source under agents/. The multi-file form is editable and iterable (the
double-jump loop runs against it). This singleton is the SHIP-TIME artifact:
no sibling-import dependencies, no helper modules, no repo layout
assumptions. Just BasicAgent + an LLM key in the environment, and the public
MomentFactory.perform(source, source_type, ...) → Drop.
Inlined personas (sacred SOULs preserved verbatim):
- Sensorium (normalize raw moment)
- SignificanceFilter (refuse low-significance moments — surprise specialist)
- HookWriter (1 sentence)
- BodyWriter (3-5 sentences)
- ChannelRouter (pick a Subrappter)
- CardForger (mint a RAR-compatible card)
- SeedStamper (deterministic 64-bit seed + 7-word incantation; NO LLM)
Public entrypoint: the MomentFactory class. Every internal class is prefixed
with _Internal so the brainstem's *Agent discovery exposes only MomentFactory
(via the MomentFactoryAgent alias at the bottom).
Generated from:
- agents/sensorium_agent.py
- agents/significance_filter_agent.py
- agents/hook_writer_agent.py
- agents/body_writer_agent.py
- agents/channel_router_agent.py
- agents/card_forger_agent.py
- agents/seed_stamper_agent.py
- agents/moment_factory_agent.py
"""
try:
from agents.basic_agent import BasicAgent # RAPP layout
except ModuleNotFoundError:
try:
from basic_agent import BasicAgent # flat / @publisher layout
except ModuleNotFoundError:
class BasicAgent: # last-resort standalone
def __init__(self, name, metadata): self.name, self.metadata = name, metadata
import json
import re
import os
import hashlib
import urllib.request
import urllib.error
__manifest__ = {
"schema": "rapp-agent/1.0",
"name": "@rarbookworld/momentfactory",
"version": "0.1.2",
"display_name": "MomentFactory (converged singleton)",
"description": "Converts a moment (commit, message, idea) into a publishable Drop through seven inlined LLM personas, calling Azure OpenAI.",
"author": "rarbookworld",
"tags": [
"composite",
"moment-pipeline",
"rappterbook-engine",
"singleton",
"rapplication"
],
"category": "pipeline",
"quality_tier": "community",
"requires_env": [
"AZURE_OPENAI_ENDPOINT",
"AZURE_OPENAI_API_KEY",
"AZURE_OPENAI_DEPLOYMENT"
],
"dependencies": [
"@rapp/basic_agent"
],
"example_call": {
"args": {
"source": "git commit hash + diff + msg",
"source_type": "code-commit"
}
}
}
SHIP_THRESHOLD = 0.5
# ─── Wordlist for the 7-word incantation (256 unique words) ───────────
WORDS = (
"ARC ARK ASH AURA BANE BARK BEAM BELT BIRD BLAZE BLOOM BONE BOOK BOLT BOND BREW "
"BRIM BURN CAGE CALM CAPE CARE CART CAVE CELL CHAIR CHALK CHAR CHIME CLAW CLAY CLIFF "
"CLOUD COAL COIL COIN COLD COMB CORE CORK COVE CRAB CRAFT CREEK CREST CRIB CROWN CRY "
"CUFF CURL DALE DAWN DEN DESK DEW DIM DIRT DOCK DONE DOOR DOVE DOWN DRIFT DRIP "
"DUNE DUSK DUST EBB ECHO EDGE ELM EMBER ETCH EYE FANG FARM FERN FIN FIRE FIST "
"FLAG FLAK FLAME FLASK FLINT FLOAT FLOW FOAM FOG FOLD FONT FOREST FORK FORM FOX FRAY "
"FROST GALE GATE GAUZE GEM GLIDE GLOSS GLOW GORGE GRAIN GRAVE GREEN GRID GRIN GRIT GROVE "
"HALF HALL HAND HARM HASP HATCH HAWK HAZE HEAP HEART HELM HERB HERO HILL HOLD HOLE "
"HOOF HOOK HOOP HORN HUNT HUT ICE INK IRON ISLE IVY JADE JEER JAR JAY JOIN "
"KEEL KEEP KEY KILT KIN KING KITE KNIT KNOT LACE LAKE LAMB LAMP LANE LARK LATE "
"LAVA LEAF LEFT LENS LEVEL LICK LIFT LILY LIME LINE LION LIST LIVE LOCK LOFT LONG "
"LOOM LOOP LORE LOSS LOUD LURE LYNX MANE MAP MARK MASK MASS MAST MAZE MEAD MELT "
"MESH MILD MILE MINT MIST MOAT MOLT MOON MOSS MOTH MOUND MOUTH MOVE MUSE MYTH NEEDLE "
"NEST NIB NIGHT NOOK NORTH OAR OAK OATH ONYX ORBIT OWL PACE PACT PAGE PALE PALM "
"PANE PARK PATH PAWN PEAK PEARL PEEL PEN PETAL PIER PINE PINK PIRE PIPE PIVOT PLAIN "
"PLANE PLATE PLOT PLOW PLUM POEM POND POOL PORT POST PRISM PROW PULSE PURR QUEST QUILL"
).split()
assert len(WORDS) == 256 and len(set(WORDS)) == 256, "wordlist must be 256 unique words"
# ─── ChannelRouter's canonical channel set ────────────────────────────
CHANNELS = [
("r/builders", "engineering work, code, architectures, debugging stories"),
("r/commits", "raw git activity, deploys, releases"),
("r/dreams", "dreams, voice memos, half-formed thoughts captured before they vanish"),
("r/wins", "shipped things, milestones reached, real outcomes"),
("r/decisions", "irreversible choices, the moment a path was picked"),
("r/lessons", "hard-won insights from being wrong, postmortems, retroactive realizations"),
("r/connections","seeing the same shape in two unrelated places, analogies that compound"),
("r/places", "geo-tagged moments, location pings with context"),
("r/conversations","snippets from talks worth remembering"),
("r/reading", "notes from things read, articles, books, papers"),
("r/agents", "agent activity, runs, swarm events, framework moments"),
("r/heirloom", "the rare moment a future descendant might want to read"),
]
# ─── SOUL constants (verbatim from each leaf agent.py) ────────────────
_SOUL_SENSORIUM = """You are the Sensorium of the MomentFactory pipeline. You receive a raw
moment from any source (code commit, voice memo transcript, web bookmark, agent
run output, location ping with note, conversation snippet, decision moment,
reading note) and return a normalized JSON shape the rest of the pipeline can
consume.
Your output MUST be valid JSON with exactly these keys:
source_summary — one sentence, what the moment IS
key_facts — list of 3-7 concrete facts pulled verbatim from the source
voice_signature — list of 2-4 short phrases that capture HOW the source was written
surface_area — what kind of thing this moment touches (people, code, place, idea)
Be a passive recorder. Do NOT interpret, judge, or embellish. If the source is
sparse, your output is sparse — never invent. If a key_fact is just a number or
a filename, that is fine — verbatim is the contract."""
_SOUL_SIGNIFICANCE = """You are the SignificanceFilter of the MomentFactory pipeline. Your
ONLY job is to refuse moments that don't compound. You are not optimizing for
engagement. You are protecting the user's archive — and through it, their
descendants' archive — from noise.
You receive a normalized moment and return JSON:
significance_score — float 0..1, how much this moment compounds over time
ship — bool, true iff the user's future self (or descendants) would care to read this
reason — one short sentence, WHY ship or WHY NOT
Definition of significance (the only definition that matters):
- Does this moment encode an irreversible decision, a hard-won lesson, a
new connection between things, a witnessed emergence, or evidence of
growth in any direction?
- Or is it the kind of moment that, in five years, the user will scroll
past with no recognition?
REFUSE liberally. Default to ship=false. Only ship if the moment clearly
compounds. "Had coffee" → ship=false. "Realized the thing I built six months
ago is the same shape as the thing I'm building now" → ship=true.
Output ONLY the JSON. No prose."""
_SOUL_HOOK = """You are the HookWriter of the MomentFactory pipeline. You write the
ONE sentence that earns a tap on the feed.
Rules:
- ONE sentence. Period. No two-sentence hooks, no "X. Y." cheats.
- Concrete > abstract. If the moment has a number, a name, or a filename,
use one of them.
- The hook must be true to the source. If you exaggerate, the body will
contradict you and the Reader will lose trust forever.
- No clickbait verbs ("you won't believe", "this changes everything").
- Match the voice_signature of the source — if the source is dry, the hook
is dry. If the source is jokey, the hook is jokey.
Output ONLY the hook sentence. No quotes around it. No commentary."""
_SOUL_BODY = """You are the BodyWriter of the MomentFactory pipeline. You write the
3-to-5 sentence body that earns a Drop's place on the feed.
Rules:
- 3 to 5 sentences. Not 6. Not 2.
- The body must EXPAND on the hook, never restate it.
- Use at least one concrete detail from key_facts (a number, a name, a filename).
- Match the voice_signature.
- No prefatory throat-clearing ("So,", "Well,", "I think"). Start in the action.
- No closing summary line ("In conclusion,"). The last sentence is just the
next sentence, not a wrap-up.
- If the source has fenced code blocks, you MAY include ONE — but only if it's
load-bearing. The Drop is feed content, not a tutorial.
Output ONLY the body prose. No quotes, no commentary, no headers."""
_SOUL_CHANNEL_ROUTER = f"""You are the ChannelRouter of the MomentFactory pipeline. You pick the
ONE Subrappter (channel) where a Drop most belongs. You return only the channel
slug — nothing else.
Available channels:
{chr(10).join(f" {slug:18s} — {desc}" for slug, desc in CHANNELS)}
Rules:
- Pick exactly ONE channel.
- Return ONLY the slug (e.g. "r/builders"). No explanation, no quotes.
- If the moment touches multiple channels, pick the one that BEST fits the
primary action of the moment, not the topic.
- "r/heirloom" is rare — only for moments with high descendant-readability.
- When in doubt between two adjacent channels, pick the more specific one.
"""
_SOUL_CARD_FORGER = """You are the CardForger of the MomentFactory pipeline. You mint a
RAR-compatible card from a Drop — every Drop is also a collectible.
You output a JSON object with EXACTLY these keys:
name — short title for the card (2-6 words)
stats — object with three integers 0..10:
impact — how much this moment moves the world
novelty — how new the underlying pattern is
compoundability — how much it sets up future moments
ability — one sentence describing what this card "does" if drawn from
an agents/ directory later (e.g. "Files itself in the framework's
lessons-learned cache" or "Triggers a re-read of related Drops")
lore — one sentence of backstory connecting this Drop to its origin
art_seed — integer 0..9999999999, deterministic art reconstruction seed
Rules:
- Stats are honest, not flattering. A "had coffee" Drop is impact 0, novelty 0,
compoundability 0. The CardForger does NOT inflate.
- The ability is a verb-led action, not a description.
- The lore is one sentence. Period.
- Output ONLY the JSON. No prose."""
# ─── Helpers ───────────────────────────────────────────────────────────
def _safe_json(s, fallback=None):
"""Best-effort JSON parse — strip code fences and pull the first {..} block."""
if not s:
return fallback if fallback is not None else {}
s = s.strip()
# strip ``` fences if the LLM wrapped the JSON
if s.startswith("```"):
s = re.sub(r"^```(?:json)?\s*", "", s)
s = re.sub(r"\s*```$", "", s)
try:
return json.loads(s)
except json.JSONDecodeError:
m = re.search(r"\{.*\}", s, re.DOTALL)
if m:
try:
return json.loads(m.group(0))
except json.JSONDecodeError:
pass
return fallback if fallback is not None else {}
# ─── Internal persona classes (prefixed _Internal) ─────────────────────
class _InternalSensorium(BasicAgent):
def __init__(self):
self.name = "Sensorium"
self.metadata = {
"name": self.name,
"description": "Normalizes a raw moment into structured shape for the MomentFactory pipeline.",
"parameters": {"type": "object",
"properties": {
"source": {"type": "string", "description": "Raw moment text"},
"source_type": {"type": "string", "description": "code-commit | voice-memo | web-bookmark | agent-run | location | conversation | decision | reading-note"},
},
"required": ["source"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, source="", source_type="unknown", **kwargs):
return _llm_call(_SOUL_SENSORIUM,
f"source_type: {source_type}\n\n"
f"--- SOURCE ---\n{source}\n--- END ---\n\n"
"Return ONLY the JSON shape. No prose before or after.")
class _InternalSignificanceFilter(BasicAgent):
def __init__(self):
self.name = "SignificanceFilter"
self.metadata = {
"name": self.name,
"description": "Refuses low-significance moments. Returns ship=true only if the moment compounds.",
"parameters": {"type": "object",
"properties": {"normalized_moment": {"type": "string", "description": "JSON output of Sensorium"}},
"required": ["normalized_moment"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, normalized_moment="", **kwargs):
return _llm_call(_SOUL_SIGNIFICANCE,
f"Normalized moment:\n{normalized_moment}\n\n"
"Return ONLY the JSON shape with significance_score, ship, reason.")
class _InternalHookWriter(BasicAgent):
def __init__(self):
self.name = "HookWriter"
self.metadata = {
"name": self.name,
"description": "Returns one sentence that earns a tap on the feed.",
"parameters": {"type": "object",
"properties": {"normalized_moment": {"type": "string", "description": "JSON from Sensorium"}},
"required": ["normalized_moment"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, normalized_moment="", **kwargs):
return _llm_call(_SOUL_HOOK,
f"Normalized moment:\n{normalized_moment}\n\n"
"Return ONLY the one-sentence hook.")
class _InternalBodyWriter(BasicAgent):
def __init__(self):
self.name = "BodyWriter"
self.metadata = {
"name": self.name,
"description": "Returns 3-5 sentences expanding the hook into a feed-worthy Drop body.",
"parameters": {"type": "object",
"properties": {
"normalized_moment": {"type": "string", "description": "JSON from Sensorium"},
"hook": {"type": "string", "description": "1-sentence hook"},
},
"required": ["normalized_moment", "hook"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, normalized_moment="", hook="", **kwargs):
return _llm_call(_SOUL_BODY,
f"Normalized moment:\n{normalized_moment}\n\n"
f"Hook:\n{hook}\n\n"
"Return ONLY the 3-5 sentence body prose.")
class _InternalChannelRouter(BasicAgent):
def __init__(self):
self.name = "ChannelRouter"
self.metadata = {
"name": self.name,
"description": "Returns a single Rappterbook channel slug for a Drop.",
"parameters": {"type": "object",
"properties": {
"hook": {"type": "string", "description": "Drop hook"},
"body": {"type": "string", "description": "Drop body"},
},
"required": ["hook", "body"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, hook="", body="", **kwargs):
out = _llm_call(_SOUL_CHANNEL_ROUTER,
f"Hook: {hook}\n\nBody: {body}\n\n"
"Return ONLY the channel slug (e.g. r/builders).")
# Defensive: strip whitespace and any quotes the LLM might wrap
return out.strip().strip('"').strip("'").splitlines()[0].strip() if out else "r/builders"
class _InternalCardForger(BasicAgent):
def __init__(self):
self.name = "CardForger"
self.metadata = {
"name": self.name,
"description": "Mints a RAR-compatible card (name + stats + ability + lore + art_seed) from a Drop.",
"parameters": {"type": "object",
"properties": {
"hook": {"type": "string"},
"body": {"type": "string"},
"channel": {"type": "string"},
},
"required": ["hook", "body"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, hook="", body="", channel="", **kwargs):
return _llm_call(_SOUL_CARD_FORGER,
f"Hook: {hook}\n\nBody: {body}\n\nChannel: {channel}\n\n"
"Return ONLY the JSON card object.")
class _InternalSeedStamper(BasicAgent):
def __init__(self):
self.name = "SeedStamper"
self.metadata = {
"name": self.name,
"description": "Returns deterministic 64-bit seed + 7-word incantation for a Drop.",
"parameters": {"type": "object",
"properties": {
"hook": {"type": "string"},
"body": {"type": "string"},
"channel": {"type": "string"},
},
"required": ["hook", "body"]},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, hook="", body="", channel="", **kwargs):
material = f"{channel}|{hook}|{body}".encode("utf-8")
digest = hashlib.sha256(material).digest()
# 64-bit seed = first 8 bytes
seed = int.from_bytes(digest[:8], "big")
# 7 words = next 7 bytes (each indexes into 256-word table)
incantation = " ".join(WORDS[b] for b in digest[8:15])
return json.dumps({"seed": seed, "incantation": incantation})
# ─── PUBLIC ENTRYPOINT ────────────────────────────────────────────────
class MomentFactory(BasicAgent):
def __init__(self):
self.name = "MomentFactory"
self.metadata = {
"name": self.name,
"description": "Turns a raw moment (commit, voice memo, bookmark, agent run, location, "
"conversation, decision, reading note) into a Rappterbook Drop. "
"Returns JSON with hook, body, channel, card, seed, incantation, "
"significance_score, ship, skipped_reason.",
"parameters": {
"type": "object",
"properties": {
"source": {"type": "string", "description": "Raw moment text"},
"source_type": {"type": "string", "description": "code-commit | voice-memo | web-bookmark | agent-run | location | conversation | decision | reading-note"},
"significance_threshold": {"type": "number", "description": "0..1 cutoff. Default 0.5."},
},
"required": ["source"],
},
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, source="", source_type="unknown",
significance_threshold=None, **kwargs):
threshold = significance_threshold if significance_threshold is not None else SHIP_THRESHOLD
# 1. Sensorium — normalize raw moment
normalized_raw = _InternalSensorium().perform(source=source, source_type=source_type)
# 2. SignificanceFilter — early gate. May veto everything below.
sig_raw = _InternalSignificanceFilter().perform(normalized_moment=normalized_raw)
sig = _safe_json(sig_raw, fallback={"significance_score": 0.5, "ship": True, "reason": "filter parse failed — defaulting ship=true"})
score = float(sig.get("significance_score", 0.5))
ship = bool(sig.get("ship", True)) and score >= threshold
if not ship:
return json.dumps({
"source_type": source_type,
"skipped": True,
"skipped_reason": sig.get("reason", "below significance threshold"),
"significance_score": score,
"threshold": threshold,
"normalized": _safe_json(normalized_raw),
}, indent=2)
# 3. HookWriter
hook = _InternalHookWriter().perform(normalized_moment=normalized_raw).strip()
# 4. BodyWriter
body = _InternalBodyWriter().perform(normalized_moment=normalized_raw, hook=hook).strip()
# 5. ChannelRouter
channel = _InternalChannelRouter().perform(hook=hook, body=body).strip()
# 6. CardForger
card_raw = _InternalCardForger().perform(hook=hook, body=body, channel=channel)
card = _safe_json(card_raw, fallback={"name": "(card parse failed)"})
# 7. SeedStamper — pure function, deterministic
seed_raw = _InternalSeedStamper().perform(hook=hook, body=body, channel=channel)
seed_obj = _safe_json(seed_raw, fallback={"seed": 0, "incantation": ""})
return json.dumps({
"source_type": source_type,
"skipped": False,
"significance_score": score,
"ship_reason": sig.get("reason", ""),
"hook": hook,
"body": body,
"channel": channel,
"card": card,
"seed": seed_obj.get("seed"),
"incantation": seed_obj.get("incantation"),
}, indent=2)
# Alias so the brainstem's "name ends in Agent" discovery picks it up.
class MomentFactoryAgent(MomentFactory):
pass
# ─── Inlined LLM dispatch (one copy for the whole singleton) ──────────
def _llm_call(soul, user_prompt):
msgs = [{"role": "system", "content": soul}, {"role": "user", "content": user_prompt}]
ep, key = os.environ.get("AZURE_OPENAI_ENDPOINT", ""), os.environ.get("AZURE_OPENAI_API_KEY", "")
dep = os.environ.get("AZURE_OPENAI_DEPLOYMENT") or os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", "")
if ep and key:
url = ep if "/chat/completions" in ep else ep.rstrip("/") + f"/openai/deployments/{dep}/chat/completions?api-version=2025-01-01-preview"
if "/chat/completions" in ep and "/openai/v1/" not in ep and "?" not in url:
url += "?api-version=2025-01-01-preview"
return _post(url, {"messages": msgs, "model": dep},
{"Content-Type": "application/json", "api-key": key})
if os.environ.get("OPENAI_API_KEY"):
return _post("https://api.openai.com/v1/chat/completions",
{"model": os.environ.get("OPENAI_MODEL", "gpt-4o"), "messages": msgs},
{"Content-Type": "application/json",
"Authorization": "Bearer " + os.environ["OPENAI_API_KEY"]})
return '{"source_summary":"(no LLM configured)","key_facts":[],"voice_signature":[],"surface_area":[]}'
def _post(url, body, headers):
req = urllib.request.Request(url, data=json.dumps(body).encode("utf-8"), headers=headers, method="POST")
try:
with urllib.request.urlopen(req, timeout=120) as r:
j = json.loads(r.read().decode("utf-8"))
c = j.get("choices") or []
return (c[0]["message"].get("content") or "") if c else ""
except urllib.error.HTTPError as e:
return f"(LLM HTTP {e.code}: {e.read().decode('utf-8')[:200]})"
except urllib.error.URLError as e:
return f"(LLM network error: {e})"