Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,6 @@ htmlcov/
bandit-report.json

# Temp / local dev files
branch_structure.json
temp_auto_push.bat
temp_interactive_push.bat

# Fonts (generated/local)
public/fonts/
63 changes: 63 additions & 0 deletions community/voice-shadow/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Voice Shadow

Get honest, unfiltered feedback on any idea — startup pitch, life decision, business plan, creative project. Voice Shadow uses Venice's uncensored model to give you the structured critique that polite people and safety-tuned AIs won't.

## Trigger Phrases

- "shadow my idea"
- "be brutally honest"
- "tear this apart"
- "tear apart my [idea]"
- "honest critique"
- "find the flaws"
- "stress test my [plan]"
- "reality check my [idea]"
- "poke holes in [this]"
- "what's wrong with my [idea]"
- "sanity check my [plan]"
- "brutal feedback"
- "voice shadow"
- "honest feedback on [this]"

## What It Does

1. You pitch any idea in natural speech — one sentence or several
2. Voice Shadow delivers a 4-part structured critique:
- The core flaw in the logic
- The first objection anyone will raise
- The hidden assumption that might be wrong
- The one angle that actually has potential
3. You can ask it to dig deeper on any specific point
4. Bring another idea without restarting — just pitch it

## Example Session

> "shadow my idea"
>
> *Ready. Pitch me the idea — startup, plan, decision, anything.*
>
> "I want to build an app that connects freelancers with local businesses — like Fiverr but hyperlocal and focused on in-person services"
>
> *The core flaw is that in-person hyperlocal kills the scale advantage that makes marketplaces work — you're adding the friction of geography without a clear reason why businesses prefer you over Craigslist. Anyone who hears this will immediately ask why a local business needs a platform for tasks they currently solve with a phone call. Your assumption that businesses want a new discovery channel for local freelancers may be wrong — most already have a trusted person they call. The part that actually works is the trust layer — if you can build verified reviews for in-person work, that's the real gap.*
>
> "dig into the trust layer part"
>
> *Local service trust is unsolved because review platforms like Yelp are review-bombed and one-sided. If you require job completion confirmation from both sides before a review posts, you get verified two-way accountability — that's the moat Fiverr never built for in-person work.*

## Setup

Add your Venice API key in OpenHome settings:

| Key | Value |
|-----|-------|
| `venice_api_key` | Your Venice API key from [venice.ai](https://venice.ai) |

## Storage

Voice Shadow saves up to 10 past idea critiques locally so sessions are preserved. No data leaves your device except the Venice API call.

## What Makes It Different

- **Not a debate** — `debate-partner` takes an opposing side in structured rounds with scoring. Voice Shadow evaluates *your* material with no sides, no rounds, no scoreboard.
- **Not a game** — `hot-take-machine` generates AI opinions on random pop-culture topics as a competitive game. Voice Shadow is advisor mode: user-led, idea-specific, improvement-focused.
- **Genuinely uncensored** — Uses Venice's `abliteration-abliterated-model-large-v2` with Venice's system prompt disabled. No safety hedging, no "with some refinement this could work" qualifiers.
Empty file.
216 changes: 216 additions & 0 deletions community/voice-shadow/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
import requests
from datetime import datetime
from src.agent.capability import MatchingCapability
from src.agent.capability_worker import CapabilityWorker
from src.main import AgentWorker

VENICE_BASE = "https://api.venice.ai/api/v1"
VENICE_MODEL = "abliteration-abliterated-model-large-v2"
STORAGE_KEY = "voice_shadow_sessions"
MAX_SESSIONS = 10

HOTWORDS = {
"shadow my idea", "shadow this idea", "voice shadow",
"brutal feedback", "be brutally honest", "tear this apart",
"tear apart my", "honest critique", "find the flaws",
"stress test my", "reality check my", "poke holes in",
"what's wrong with my", "whats wrong with my",
"sanity check my", "honest feedback on",
}

EXIT_WORDS = {"stop", "exit", "bye", "goodbye"}
EXIT_PHRASES = {
"that's all", "all done", "never mind", "i'm done", "no thanks",
"no more", "i quit", "let's stop", "i'm done here",
}

CRITIQUE_PROMPT = (
"You are a candid advisor reviewing someone's idea before the world tears it apart. "
"Respond in exactly 4 spoken sentences — no bullet points, no headers, pure prose: "
"1. The single biggest flaw in the core logic. "
"2. The first objection anyone who hears this will immediately raise. "
"3. The key assumption being made that could be completely wrong. "
"4. The one element that actually has real potential. "
"Rules: no hedging words, no 'however' or 'that said' or 'with some refinement'. "
"Be specific. Keep it under 100 words — it will be read aloud."
)

DRILLDOWN_PROMPT = (
"You gave honest feedback on an idea. The person wants to go deeper on one specific point. "
"Be equally direct and specific — no softening. Under 60 words — it will be spoken aloud."
)


class VoiceShadow(MatchingCapability):
worker: AgentWorker = None
capability_worker: CapabilityWorker = None

# Do not change following tag of register capability
# {{register capability}}

def does_match(self, text: str) -> bool:
t = text.lower()
return any(hw in t for hw in HOTWORDS)

def call(self, worker: AgentWorker):
self.worker = worker
self.capability_worker = CapabilityWorker(self.worker)
self.worker.session_tasks.create(self._run())

async def _run(self):
try:
self._venice_key = self.capability_worker.get_api_keys("venice_api_key") or ""
await self.capability_worker.wait_for_complete_transcription()

await self.capability_worker.speak(
"Ready. Pitch me the idea — startup, plan, decision, anything."
)

outer_running = True
while outer_running:
idea = await self.capability_worker.user_response()
if not idea or self._is_exit(idea):
break

if len(idea.split()) < 8:
await self.capability_worker.speak(
"Give me a bit more — what exactly are you proposing?"
)
idea = await self.capability_worker.user_response()
if not idea or self._is_exit(idea):
break

await self.capability_worker.speak("Give me a second.")

critique = self._call_venice(CRITIQUE_PROMPT, f"Idea: {idea}")
if not critique:
await self.capability_worker.speak(
"I couldn't reach my analysis engine — try again in a moment."
)
break

await self.capability_worker.speak(critique)
self._save_session(idea, critique)

while True:
await self.capability_worker.speak(
"Want me to dig into any of that? Or bring me another idea."
)
followup = await self.capability_worker.user_response()
if not followup or self._is_exit(followup):
outer_running = False
break

intent = self._classify_intent(followup)
if intent == "EXIT":
outer_running = False
break
elif intent == "NEW_IDEA":
idea = followup
if len(idea.split()) < 8:
await self.capability_worker.speak(
"Give me a bit more — what exactly are you proposing?"
)
idea = await self.capability_worker.user_response()
if not idea or self._is_exit(idea):
outer_running = False
break
await self.capability_worker.speak("Give me a second.")
critique = self._call_venice(CRITIQUE_PROMPT, f"Idea: {idea}")
if not critique:
await self.capability_worker.speak(
"I couldn't reach my analysis engine — try again in a moment."
)
outer_running = False
break
await self.capability_worker.speak(critique)
self._save_session(idea, critique)
else:
answer = self._call_venice(
DRILLDOWN_PROMPT,
f"Original idea: {idea}\nFeedback given: {critique}\nQuestion: {followup}",
)
await self.capability_worker.speak(
answer or "I don't have more to add on that point."
)

except Exception as e:
self.worker.editor_logging_handler.error(f"[VoiceShadow] Error: {e}")
finally:
self.capability_worker.resume_normal_flow()

def _call_venice(self, system_prompt: str, user_content: str) -> str:
headers = {
"Authorization": f"Bearer {self._venice_key}",
"Content-Type": "application/json",
}
payload = {
"model": VENICE_MODEL,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_content},
],
"venice_parameters": {
"include_venice_system_prompt": False,
"enable_web_search": "off",
},
}
try:
resp = requests.post(
f"{VENICE_BASE}/chat/completions", headers=headers, json=payload, timeout=30
)
if resp.status_code != 200:
self.worker.editor_logging_handler.error(
f"[VoiceShadow] Venice error: {resp.status_code}"
)
return ""
return (
(resp.json().get("choices") or [{}])[0]
.get("message", {})
.get("content") or ""
).strip()
except Exception as e:
self.worker.editor_logging_handler.error(f"[VoiceShadow] Venice call failed: {e!r}")
return ""

def _classify_intent(self, text: str) -> str:
raw = (
self.capability_worker.text_to_text_response(
"Classify this response as exactly one of: DRILL, NEW_IDEA, EXIT.\n"
"DRILL = asking about a specific point in the feedback just given.\n"
"NEW_IDEA = presenting an entirely new idea to evaluate.\n"
"EXIT = done, wants to stop.\n"
"Reply with ONLY the label, nothing else.\n"
f"Input: {text}"
)
or "DRILL"
).strip().upper().split()[0]
return raw if raw in {"DRILL", "NEW_IDEA", "EXIT"} else "DRILL"

def _save_session(self, idea: str, critique: str):
raw = self.capability_worker.get_single_key(STORAGE_KEY)
stored = (raw.get("value", raw) if raw else {}) or {}
sessions = stored.get("sessions", [])
sessions.append({
"idea": idea,
"critique": critique,
"timestamp": datetime.utcnow().isoformat(),
})
if len(sessions) > MAX_SESSIONS:
sessions = sessions[-MAX_SESSIONS:]
data = {"sessions": sessions}
try:
result = self.capability_worker.create_key(STORAGE_KEY, data)
if not (result or {}).get("success"):
self.capability_worker.update_key(STORAGE_KEY, data)
except Exception as e:
self.worker.editor_logging_handler.error(f"[VoiceShadow] Save error: {e!r}")

def _is_exit(self, text: str) -> bool:
if not text:
return True
t = text.lower().strip()
if any(p in t for p in EXIT_PHRASES):
return True
tokens = set(t.split())
return bool(tokens & EXIT_WORDS)
Loading