Skip to content

Latest commit

 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

emoody

Turn natural-language statements like "I had a rough morning" into structured mood data — a mood label, an intent, an energy level, and a confidence score — using whatever language model you prefer.

"I had a rough morning, ugh"
            │
            ▼
        emoody
            │
            ▼
{
  "mood": "frustrated",
  "intent": "shift_mood",     ← they want relief, not validation
  "energy_level": 3,          ← suggest something low-key, not hype
  "confidence": 0.85
}

Any Python app can use this. You bring your own LLM key.


Why does this exist?

A lot of apps want to do something like: figure out how the user feels, then respond well. Wellness apps. Journaling apps. Voice assistants. Music recommenders. Smart homes. They all need the same primitive — a small, reliable function that turns a sentence into a structured guess about the speaker's emotional state and what they probably want.

Until now, every team has built that primitive themselves. emoody is that primitive, packaged once, given away free.

You compose it into your app however you like. emoody doesn't pick the music, store the journal entries, or run the chat — it just classifies. Your app does the rest.


Who is this for?

  • You're building an app where the user types or speaks how they're feeling, and you want to take a sensible action in response.
  • You want structured output — typed enums and validated numbers, not a free-form blob of text from an LLM that your code has to defend against.
  • You want to swap LLM providers (Anthropic, OpenAI, a local model) without changing your app code.
  • You don't want to ship credentials in your library — you (or the developer integrating your app) bring your own.

The 60-second start

Try it offline — no API key needed

from emoody import classify_mood, MockProvider

result = classify_mood("I'm feeling pretty down today", provider=MockProvider())

print(result.mood)          # Mood.SAD
print(result.intent)        # Intent.SHIFT_MOOD   (they want to feel better)
print(result.energy_level)  # 2                   (low energy — calm, not hype)
print(result.confidence)    # 0.5

MockProvider uses simple keyword rules. It's not smart, but it lets you build, test, and demo your app without spending a cent or signing up for anything.

Use a real LLM (Anthropic Claude)

import os
os.environ["ANTHROPIC_API_KEY"] = "sk-ant-..."   # your key, not ours

from emoody import classify_mood

result = classify_mood("I had a rough morning, ugh")
# MoodIntent(mood=Mood.FRUSTRATED, intent=Intent.SHIFT_MOOD,
#            energy_level=3, confidence=0.85)

When you don't pass a provider, classify_mood defaults to Anthropic Claude (Haiku 4.5). It uses Claude's structured-output feature so the result is always a valid MoodIntent — never a malformed JSON blob.

You only need to set ANTHROPIC_API_KEY once, in your shell environment.


What you get back

A MoodIntent is just a Pydantic model. Four fields:

Field What it is Possible values
mood How the user is feeling happy, sad, frustrated, anxious, calm, energetic, neutral
intent What kind of response would help match_mood (lean in), shift_mood (cheer me up / calm me down), focus, sleep, party
energy_level Suggested response energy 0 (quiet/calm) → 10 (high-energy)
confidence Model's confidence in its guess 0.01.0

The schema is opinionated on purpose. v0.1 has a small fixed vocabulary so downstream apps can reason about it. You can pass a custom system prompt to bias the model, but you can't redefine the enums yet — that's planned for v0.2.


How it flows

your app             emoody              provider (Anthropic / OpenAI / yours)
   │                   │                            │
   │ classify_mood()   │                            │
   ├──────────────────▶│                            │
   │                   │   structured-output call   │
   │                   ├───────────────────────────▶│
   │                   │                            │
   │                   │     valid MoodIntent       │
   │                   │◀───────────────────────────│
   │                   │                            │
   │   MoodIntent      │                            │
   │◀──────────────────│                            │

emoody is a thin orchestration layer. The real work is the provider's. Swap providers freely.


Bring your own LLM

The Anthropic provider is just one implementation of a tiny interface:

class Provider(Protocol):
    def classify(self, text: str, *, system_prompt: str) -> MoodIntent: ...

Anything matching that shape works. Here's a stub showing how you'd plug in your own — OpenAI, a local model via Ollama, a deterministic fake for tests, whatever:

from emoody import classify_mood
from emoody.schema import Intent, Mood, MoodIntent

class MyProvider:
    def classify(self, text, *, system_prompt):
        # call your LLM, parse the response, return a MoodIntent
        return MoodIntent(
            mood=Mood.HAPPY, intent=Intent.MATCH_MOOD,
            energy_level=7, confidence=0.9,
        )

result = classify_mood("hello world", provider=MyProvider())

The protocol is runtime_checkable, so isinstance(MyProvider(), Provider) works for tests.


Command line

emoody classify "I'm exhausted but want to push through" --provider mock
{
  "mood": "neutral",
  "intent": "sleep",
  "energy_level": 1,
  "confidence": 0.5
}

Drop --provider mock to use the default Anthropic provider (needs ANTHROPIC_API_KEY).


Install

# Core + mock provider, nothing else
pip install git+https://github.com/aniruddhgoteti/eMoody

# Add the Anthropic provider
pip install "emoody[anthropic] @ git+https://github.com/aniruddhgoteti/eMoody"

# Everything: Anthropic + Streamlit demo + dev tools
pip install "emoody[dev] @ git+https://github.com/aniruddhgoteti/eMoody"

PyPI release coming once the schema settles. Until then, install from this repo directly.

Requires Python 3.10+.


Try the demo

pip install "emoody[demo,anthropic] @ git+https://github.com/aniruddhgoteti/eMoody"
streamlit run examples/streamlit_demo.py

Open the URL Streamlit prints. Type a sentence. Watch it get classified. Toggle to the Anthropic provider if you want to use a real LLM (you'll need your own key).


The backstory

emoody began as the winning entry to the 2018 Sony Audio Control Hackathon. The original was a single Python script that listened to your voice through a microphone, classified your mood with Wit.ai, and made a Sony smart speaker play music to match. Cute hackathon prototype — and badly out of date by 2026 (Wit.ai is being sunset by Meta, and the script had hardcoded paths and credentials all over).

The original lives at archive/eMoody-2018.py for posterity.

This rewrite throws out the speaker integration, the voice capture, and the cloud NLP, and keeps just the idea — turn a feeling expressed in plain English into structured data — packaged as a small library other developers can build on.


FAQ

Does this run a model on my machine? No. emoody itself is just glue code. The classification happens in whatever LLM provider you pick. The mock provider is the only "local" option, and it's not a real model — just keyword rules useful for tests and offline demos.

Can I use this without any API key? Yes — use MockProvider. It's fully deterministic and good enough to develop your app's UI and logic before you pay for any LLM.

Does this store anything? No. emoody has no database, no logs, no telemetry. Each call is stateless.

Can I add a custom mood like nostalgic or bittersweet? Not in v0.1 — the enum is fixed. v0.2 plans to support custom vocabularies via a MoodSchema(...) constructor. Until then, you can build a richer enum on top.

What happens if the LLM returns garbage? The Anthropic provider uses Claude's tool-use feature, which structurally enforces the JSON shape — Claude can't return a malformed MoodIntent. If you write your own provider, you need to enforce the shape yourself, or the call will raise a Pydantic validation error.

How much does the Anthropic provider cost? Roughly $0.0005 per classification at list price (Claude Haiku 4.5, ~300 input tokens + 80 output tokens). With prompt caching that drops to ~$0.0002. A user doing 50 classifications a month costs you about two cents.

How do I verify it really works with the Anthropic API?

export ANTHROPIC_API_KEY=...
pip install -e ".[dev]"
pytest -m smoke

That's an opt-in test (skipped by default and never run in CI) that pushes five varied phrases through Claude and checks the moods are sensible.


Status

v0.1 — alpha. The schema may shift in v0.2 once real downstream apps integrate. Pin the version if that scares you.

Roadmap lives in BACKLOG.md. Headlines: OpenAI + local-LLM providers next, then voice helpers, then async.


License

MIT.


That's it. Build something.

About

Winning prototype of Sony Audio Control Hackathon.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages