malloc-app / README.md
sudhanshu388's picture
Update README.md
a133428 verified
|
Raw
History Blame Contribute Delete
17.2 kB

A newer version of the Gradio SDK is available: 6.24.0

Upgrade
metadata
title: Malloc Career Intelligence OS
emoji: 
colorFrom: purple
colorTo: indigo
sdk: gradio
sdk_version: 4.44.0
app_file: app.py
pinned: false
license: mit

malloc() — AI Career & Intelligence OS

Voice-enabled personal AI assistant with long-term memory. Scoped as a 5-day MVP: text + voice input, memory extraction, RAG retrieval, remember/forget/update commands, a simple dashboard. Explicitly out of scope for the MVP: auth, microservices, memory consolidation, multi-agent setups, advanced reranking, Kubernetes, custom model training. This README reflects Day 1 only (plain chat loop, no memory); sections grow as each day lands. Do not describe unimplemented days as if they exist.

Problem & motivation

General-purpose chat assistants forget everything between sessions. malloc()'s goal is a personal assistant that can remember useful facts about a user over time, retrieve the right memory at the right moment, update memories when they change, resolve conflicts when new information contradicts old, and forget things on request. Stage 1 builds the plain chat loop everything else sits on top of — no memory yet, deliberately.

Architecture (Day 1)

User → Streamlit → FastAPI (/chat) → conversation/message storage (SQLite) → LLM → Response

LangChain and FAISS are in the eventual stack (for Day 2/3 memory extraction and RAG retrieval) but deliberately not installed yet — Day 1 has nothing for them to do.

Two things are already separated even though Stage 1 doesn't need it yet:

  • Conversation history (this stage) — the raw back-and-forth of one conversation, stored in full.
  • Long-term memory (Stage 3+) — a small, curated set of durable facts extracted from conversation history. Not built yet.

This separation is the single most important architectural decision in the project (see Stage 3 plan below) — conversation history and long-term memory must never be conflated, so the schema and code paths are kept apart from day one.

Memory types, lifecycle, retrieval, conflict resolution, agent

architecture, voice architecture, privacy model, evaluation methodology

Not implemented yet. These sections will be filled in as Stages 3–12 land.

API (Day 1-2)

  • POST /chat{user_external_id, conversation_id?, message}{conversation_id, reply}. Creates a user/conversation on first contact; omit conversation_id to start a new conversation, pass it back to continue one. After the reply is generated, runs best-effort memory extraction on the turn (see below).
  • GET /chat/{conversation_id} — full message history for a conversation.
  • GET /memories/{user_external_id} — list that user's active stored memories.
  • POST /media/transcribe — upload an audio file → {text} (faster-whisper).
  • POST /media/caption — upload an image file → {caption} (BLIP image captioning).
  • POST /ats/check (and /api/ats/check) — audit resume parseability & ATS compatibility.
  • POST /matcher/suggest-edits (and /api/resume/suggest-edits) — generate opt-in bullet rewrites, evidenced keyword additions, and clarity polish with strict fabrication guardrail.
  • POST /insights/analyze (and /api/company/insights) — multi-layer company size classification, industry tagging, culture sentiment scoring, and interview intelligence.
  • GET /insights/{company_name} — quick company intelligence lookup.
  • GET /health — liveness check.

Company Insights Engine with Pretrained Transformers Classifiers

Multi-layer company analysis integrating zero-shot natural language inference, specialized text classification, and quantifiable sentiment analysis without violating anti-scraping boundaries.

Multi-Layer Model Pipeline

  1. Layer 1 — Zero-Shot Company Size Classification:
    • Model: facebook/bart-large-mnli (large zero-shot NLI pipeline).
    • Candidate Labels: "large multinational corporation", "early-stage startup", "established mid-size company".
    • Combines scraped About narrative with structured numeric facts (founded_year, employee_count_estimate).
    • Disagreement Guardrail: Cross-checks model label against extracted numbers (e.g. flags conflict if model predicts "startup" for a 50,000-employee company).
  2. Layer 1b — Industry Classification:
    • Model: sampathkethineedi/industry-classification (DistilBERT fine-tuned on 62 business industry categories).
    • Operates on bounded 512-character company description.
  3. Layer 2 — Official Domain & About Text Parsing:
    • Extracts structured signals while strictly adhering to ToS (no direct Glassdoor, AmbitionBox, or raw Google search scraping).
  4. Layer 3 — Culture Synthesis & Quantifiable Sentiment:
    • Model: distilbert-base-uncased-finetuned-sst-2-english (SST-2 sentiment pipeline).
    • Computes quantifiable sentiment_breakdown (positive_count, negative_count, total) alongside cited qualitative praised/criticized aspects.
  5. Layer 4 — Interview Intelligence:
    • Curates key focus areas (Architecture, Clean Coding, Behavioral), hiring stages, and targeted preparation tips.

Suggested Resume Edits & Anti-Fabrication Guardrail

Directly within the Resume Matcher, generates concrete, actionable bullet rewrites and keyword placement recommendations to align an uploaded resume with a target job specification.

4-Layer Architecture

  1. Layer 1 — Skill Gap & Evidence Analysis:
    • Reuses skill extraction and keyword overlap from the Resume Matcher.
    • Cross-references missing job skills with the entire resume body: if a skill is mentioned in experience or project descriptions but absent from the explicit Skills list, it surfaces as a safe "Add to Skills section" recommendation.
  2. Layer 2 — Targeted Bullet Rewriting:
    • Model: vsr9awc/resume-optimizer (Qwen2.5-1.5B-Instruct fine-tuned on 96,000+ resume bullet optimization pairs).
    • Operates per-bullet on experience and project achievements rather than whole-document blobs.
    • Caveat: As a smaller community fine-tune, all outputs are marked as draft AI suggestions requiring human review.
  3. Layer 3 — Programmatic Fabrication Guardrail (Strict Safety Layer):
    • Programmatically verifies that no new metrics, numbers, percentages, dollar figures, or unsupported tools are hallucinated.
    • Unverified numbers are flagged with clear warnings or discarded before reaching the UI.
  4. Layer 4 — Grammar & Clarity Polish:
    • Model: AventIQ-AI/t5-small-grammar-correction (T5-small on JFLEG) for phrasing refinement.
    • Opt-in UI: Users can individually Accept (modifying active editor text) or Dismiss suggestions.

ATS (Applicant Tracking System) Checker

Scores a resume's raw parseability, formatting, and structural readability independently of any specific job description, preventing silent rejection before a human recruiter ever reviews it.

Hybrid Scoring Architecture

  1. Layer 1 — Deterministic Rule-Based Parseability (65% Weight):

    • File Format: Standard PDF/TXT/DOCX compatibility.
    • Filename Hygiene: Warns against special characters and generic names.
    • Standard Section Headers: Validates presence of Experience, Education, Skills, and Summary headers.
    • Contact Detectability: Regex-based verification of email, phone number, and LinkedIn/GitHub links.
    • Multi-Column & Table Detection: Heuristics to catch formatting artifacts that break ATS text extraction.
    • Length & Density: Configurable word count checks (150–1600 words) and bullet-point scannability ratios.
  2. Layer 2 — Content Quality via HuggingFace Models (35% Weight):

    • yashpwr/resume-ner-bert-v2: BERT token-classification pipeline extracting structured entities (Skills, Designation, Degree).
    • srivihari/resume-job-role-classifier: DistilBERT text-classification pipeline assessing role domain coherence and confidence.
    • Lazy-Loaded & Best-Effort: Models load on first use and are cached as singletons (@lru_cache). If model download or inference fails, the request returns 200 OK with Layer 1 rule checks and ML checks marked unavailable.
  3. Combined Scoring & Output:

    • overall_score (0–100) with traffic-light verdicts: ATS-Friendly (≥80), Needs Improvement (50–79), High Risk (<50).
    • Prioritized, actionable improvement recommendations list.

Job application tracker

Core structure for job tracking, follow-up reminders, and (future) reports. New table: job_applications (company, role, status, applied date, follow-up date, notes, job URL).

  • POST /jobs — log a new application. follow_up_days (default 7) sets follow_up_date = applied_date + N days automatically.
  • GET /jobs/{user_external_id} — all applications for a user.
  • GET /jobs/{user_external_id}/due-followups — applications whose follow-up date has passed and are still open (applied/interview/ no_response — not offer/rejected, which are terminal).
  • PATCH /jobs/{job_id} — update any field (status, notes, etc.).
  • DELETE /jobs/{job_id}.

Reminders are pull-based, not push-based: there's no background scheduler (APScheduler/Celery) sending notifications. The Streamlit Job Tracker tab checks due-followups on page load and shows a banner if anything's overdue. That covers the actual need for a single-user MVP without adding scheduler infrastructure — revisit only if you need a reminder to reach you without opening the app.

Not yet built: weekly/monthly reports, resume upload/parsing, and the job-post → research → rating → draft-email pipeline — these are separate features layered on top of this table, not built yet.

Memory extraction (Day 2)

After every chat turn, app/memory_extraction.py sends the turn to the LLM with a dedicated system prompt and asks for one of two actions:

{"action": "STORE", "memory_type": "semantic", "content": "...", "importance": 0.8, "confidence": 0.9}

or

{"action": "IGNORE", "memory_type": null, "content": null, "importance": null, "confidence": null}

The response is validated with a Pydantic model (MemoryExtraction) — malformed or schema-violating output raises ExtractionError rather than being trusted. STORE results are written to the memories table; IGNORE and any failure are simply skipped.

The one rule this is built around: extraction must never break chat. It runs after the chat reply has already been generated and returned, and every failure mode (LLM error, bad JSON, schema violation) is caught and logged rather than propagated — verified by test_chat_succeeds_even_if_extraction_fails.

Not yet built: UPDATE/conflict resolution (a fact contradicting an existing memory just gets extracted as a new row for now — Stage 6/7's temporal + conflict-resolution logic hasn't landed), and remember/forget commands (Day 4).

/media/* routes only produce text — they don't call the LLM. Feed the returned text into /chat yourself (that's how voice/image input is meant to compose with the existing chat flow, rather than duplicating logic inside the media routes).

Multimodal + vector store components

  • app/speech.py — faster-whisper wrapper (transcribe(audio_path)), model size base, CPU/int8.
  • app/vision.py — BLIP image captioning wrapper (caption_image(image_path)). This is captioning, not OCR — it describes an image, it doesn't read text inside one.
  • app/vectorstore.pysentence-transformers (all-MiniLM-L6-v2) embeddings + a FAISS flat index (MemoryVectorStore) with add/search. This is the piece Day 3's RAG memory retrieval will plug into.

Two things worth knowing before relying on these:

  • Model weights are not bundled — the first call to any of these downloads from Hugging Face on your machine (needs internet, and disk: torch + transformers + the model weights add up to a few GB).
  • The vector store is in-memory only right now — it does not survive a process restart. Persisting it (to a FAISS index file, or moving into pgvector per the original architecture plan) is a Day 3 task, not done yet. remove() is intentionally unimplemented for the same reason — soft-delete at the DB layer (mark inactive, filter after search) is the intended approach once real memories exist, per the temporal-memory design.

Local setup

cd memora
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env

Get a free Groq API key (no credit card needed) at https://console.groq.com/keys and put it in .env as GROQ_API_KEY. This is the default provider — no billing setup required to get running.

Prefer to use Claude instead? Set LLM_PROVIDER=anthropic and ANTHROPIC_API_KEY in .env (requires a funded console.anthropic.com account).

uvicorn app.main:app --reload

In a second terminal, start the Streamlit UI:

streamlit run streamlit_app.py

Or skip the UI and hit the API directly:

curl -X POST localhost:8000/chat \
  -H "Content-Type: application/json" \
  -d '{"user_external_id": "demo-user", "message": "Hey, who are you?"}'

Tests

pytest tests/ -v

Tests mock the LLM call, so they run without a real ANTHROPIC_API_KEY. 6/6 passing as of Stage 1.

Deployment

Not implemented yet (Stage 14 in the master plan / end of the 8-week timeline). Stage 1 is local-only.

Future improvements / roadmap

5-day MVP plan:

  • Day 1 ✅ Core chatbot + database — Streamlit → FastAPI → LLM → Response, conversation/message storage.
  • Day 2 ✅ Long-term memory extraction — structured STORE/IGNORE actions via Pydantic (app/memory_extraction.py), memories table, best-effort (never breaks chat on failure). Plus multimodal input scaffolding (/media/transcribe, /media/caption) and a vector store module (app/vectorstore.py) ready for Day 3.
  • Day 3 — RAG-based memory retrieval: wire app/vectorstore.py into the chat flow — embed stored memories on STORE, embed the incoming query, retrieve top-K relevant memories, inject into the LLM's context.
  • Day 4 — Remember / forget / update commands (explicit user control over what's stored) + wiring /media/transcribe into the chat flow.
  • Day 5 — Simple memory dashboard (list, search, edit, delete) + end-to-end pass.

Deferred beyond the MVP: authentication, memory consolidation, temporal HISTORICAL/ACTIVE status tracking, conflict resolution beyond simple STORE (an UPDATE that contradicts an existing memory currently just becomes a second row), a LangGraph multi-tool agent, advanced reranking, and a formal precision/recall/F1 evaluation harness.

Job Post Authenticity & Fraud Risk Checker

malloc() includes a hybrid 3-layer security engine for evaluating job postings against employment fraud and scam patterns (/authenticity/check and /api/jobs/authenticity-check):

3-Layer Hybrid Architecture

  1. Layer 1: Rule-Based Red-Flag Heuristics (Deterministic)
    • Regex and keyword scanning for disposable/free email contact domains (@gmail.com, @yahoo.com), private messaging interview redirection (Telegram/WhatsApp/Signal), upfront payment/processing fee demands, fake equipment checks, and URL shorteners (bit.ly, tinyurl.com).
  2. Layer 2: ML Binary Classification Signal
    • Model: AventIQ-AI/BERT-Spam-Job-Posting-Detection-Model (bert-base-uncased fine-tuned on fake-vs-real job postings).
    • Documented Model Limitations:
      • 128-token max context window (~100 words): Focuses on job title, lead paragraph, and red-flag dense sections.
      • Precision on "Fake Job" class is ~0.81: Real-world class imbalance means "Fake Job" prediction represents ~4-in-5 reliability.
      • English language trained: Evaluated only on English postings.
  3. Layer 3: RAG-Augmented LLM Reasoning
    • Curated knowledge base of 18+ employment fraud patterns (advance equipment check fraud, phishing for banking details, pyramid/MLM schemes, reshipping mule operations).
    • Embedded with all-MiniLM-L6-v2 cosine similarity retrieval (top-k=4).
    • Structured Pydantic validation requiring exact cited quotes from the posting.

Critical Framing & Disclaimer

  • Results are always framed as objective risk indicators with cited evidence, never as unqualified factual claims about a company.
  • A permanent disclaimer informs users that this is a heuristic decision-support signal, not a definitive verification.

Engineering rules (apply at every stage)

  • Don't over-engineer the first version.
  • Don't store every conversation as memory.
  • Never let the LLM directly execute database mutations.
  • Validate structured LLM outputs with Pydantic.
  • Keep memory retrieval separate from conversation history.
  • Keep current conversation context separate from long-term memory.
  • Track memory versions.
  • Make memory deletion reliable.
  • Never fabricate memory — if nothing relevant exists, say so.