This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

Team: @lydiazhang135, @shiyi_jiang_92dc7754aa4d9 and me.

Your partner sends two characters: 没事, "it's nothing." You have typed and deleted three replies. Is it really nothing? Do they want an apology, an explanation, or some space? And whatever you send next, do you want a cloud service to read the whole conversation first?

TL;DR

  • Paste your partner's message. A 322M model, Laya, reads the subtext in under half a second, and Gemma 4 E2B writes two replies; on a laptop CPU the first is done in about 3.6 s. You edit one and send it yourself.
  • Both models run on your own machine: by default nothing you paste leaves it, and with no per-message cost the app can sell for a one-time US$14.99 (¥68 in China), where the main cloud apps like it charge US$29.99–79.99 a year.
  • Fine-tuning the judge for about US$3 lifted its macro-F1 from 0.33 to 0.57 and its safety recall from 0% to 62% (the bar I set before training is 90%). The fine-tuned Gemma won 133 and lost 67 of 297 blind comparisons with stock Gemma (the rest tied). The app has used both since 18 October; a final, held-back exam comes next.
  • Try it in your browser (stand-in models, sample data), and recompute the evaluation numbers below with one command: npm run reproduce.

What I Built

Laya-Heart is a copilot for couples' chats. It runs as a web page on your own computer.

  1. Copy what your partner said, switch to the page and press Ctrl+V.
  2. Within a second, a small Laya card appears under their message: one to three short questions about it, with probabilities (Does "没事" really mean it's fine? What do they need now?), or a danger level from 1 to 10.
  3. Gemma 4 E2B starts writing two replies straight away. On a laptop CPU, the first is complete in about 3.6 s.
  4. Pick one, edit it, press Enter to copy it, and paste it into your chat app yourself.

Laya-Heart connects to no chat app and never sends a message for you. With the default settings, nothing you paste leaves your machine.

Each person you talk to gets a profile: their chat, saved on your PC, and a short "book" you write about them (nicknames, sore points, the day you met). When a new message touches on something in the book, Gemma gets the matching sentences. The app also saves facts from the chat on its own, in the chat's own words, with undo, and pins down relative dates: 下周六(10-24)是我生日, "next Saturday (Oct 24) is my birthday."

Safety rules are written in code, not left to a model. After every card, Laya answers one more, hidden question about safety, and the code also checks the chat for keywords. Signs of self-harm, or of abuse and control, bring up a notice; for self-harm, the reply options also include one written by a person, not by Gemma. If the chat looks like it involves a minor, nothing is drafted until you confirm that you are both adults. On the 300-chat practice exam described below, the notice came up on 13 of the 21 risky chats (the keyword rules alone caught 4) and on 1.8% of ordinary chats.

Who would pay for it? People who freeze over a text from someone they care about: new couples, long-distance couples, anxious texters. What I'd sell them is what a chatbot tab doesn't give: privacy (the chat stays on the PC), speed (one paste, replies in seconds, with the history and profile already there), and no subscription.

How many, and for how much? From public census and survey data, about 39–51 million Americans aged 20–34 have a partner and a smartphone, and 17–27 million of them can use a Windows PC. For China, the same estimate gives 138–162 million and 41–94 million. The main apps in this space (RIZZ, WingAI, YourMove and others) sell subscriptions: US$29.99–79.99 a year on the US App Store. I'd charge once: US$14.99, or ¥68 in China, for the ready-to-run app and its updates, while the code stays MIT. The market-size note has the sources and the arithmetic.

Demo

▶ Watch the 62-second demo (MP4, 4 MB, narrated with ElevenLabs, plays in the browser). Both models run on my laptop's CPU, the couple is made up, and nothing is sped up or cut. It was recorded on 16 October, before fine-tuning (zero-shot Laya, stock Gemma), by the script that drives the page. Afterwards I redrew the caption strip and the closing card in the same style, so the captions match the narration word for word; the app on screen is the recording itself.

Laya-Heart: Laya's card under her message, two replies from Gemma, and the memory they used

▶ Try the online demo (中文), labeled sample data: the app's real page in your browser tab, with stand-ins for the two models, so its cards and replies are placeholders. Paste a line, pick a reply, edit it, add a profile; nothing leaves the tab. It is hosted on Render, with a mirror on Hugging Face.

Code

GitHub logo sykyyds3739 / laya-heart

Local-first couple-chat copilot: Laya reads the room, Gemma drafts, you press send. Built on Jev Chat Assistant.

Laya-Heart

A copilot for couple chats that runs as a web page on your own PC. Copy what your partner said, switch to the page and press Ctrl+V: a Laya card (a 322M-parameter decision model) hangs under their message within about half a second — one to three short questions about what they just said, with probabilities — and Gemma 4 E2B starts writing two replies straight away, a few seconds later they are on the right. Use this puts one into the I said box, where you can edit it; Enter copies it and adds it to the chat, and you paste it into your chat app yourself It connects to no chat app, sends nothing for you, and with default settings the chat never leaves your machine. Each person you chat with has a profile: their chat is kept on your PC, and you can write a short…

The app is MIT-licensed and built on Jev Chat Assistant (MIT). Laya-Heart is not made or endorsed by the authors of Jev Chat Assistant; CREDITS.md lists exactly what I borrowed. The fine-tuning kit is on the train-kit branch (tag train-kit-rc1), with a Chinese manual for each model. The models are on Hugging Face: Laya, zero-shot and fine-tuned v3, as ONNX, and the fine-tuned Gemma v4 as GGUF.

One command reproduces the evaluation. npm run reproduce recomputes the fine-tuning report's 62 evaluation numbers from the raw data in eval/, offline, in about two seconds. npm run reproduce -- --models also downloads both Laya models at a pinned revision and re-runs them on your CPU: 68 checks, every probability within 0.001 of the recorded run.

How I Built It

Open piece Job License
Laya-multilingual by Convai Innovations (322M, built on mmBERT), fine-tuned here reads their message: probabilities for each card question Apache-2.0 (mmBERT: MIT)
Gemma 4 E2B by Google, fine-tuned here (Q4_K_M GGUF) writes the replies; picks facts worth keeping Apache-2.0
llama.cpp llama-server runs Gemma on the CPU MIT
onnxruntime-node and Laya's laya-ts runtime run Laya in a worker process MIT; Apache-2.0
DeepSeek-V4.1-Flash (through its API); Qwen3-14B/32B-AWQ (vLLM on Kaggle) training only: teachers that write and label practice chats MIT; Apache-2.0

The server is plain Node.js with one dependency (onnxruntime-node), and it listens on 127.0.0.1 only.

I built Laya-Heart with an AI coding agent (Claude Code): under my direction it wrote much of the code, ran the training and recorded the demo, and it helped draft this post.

Making a 2B model feel instant on a laptop CPU

On a laptop CPU, Gemma 4 E2B reads a prompt at about 55 tokens/s and writes about 12 tokens/s, so reading was most of the wait. At first I ran Gemma through Ollama's chat API, which reuses Gemma 4's sliding-window cache only when the prompt is exactly the same. So every new message meant re-reading the whole 400–475-token prompt: 6.4–9.4 s. Three changes removed most of that wait:

  • I start llama.cpp's llama-server myself with --swa-full, so it keeps everything it has already read.
  • The prompt is laid out for the cache: fixed rules first, then the chat, which only grows at the end, then a short tail that turns Laya's answers into instructions. A new message now costs 62–105 new tokens instead of 400+.
  • Replies start the moment the card is done, stream as they are written, and stop after two.

On my laptop (Intel i5-13420H, 16 GB of RAM, no discrete GPU), two five-turn runs with the fine-tuned models, traced in Sentry, gave median times of 0.44 s to the whole card, 3.6 s to the first complete reply and 5.8 s to both replies. That was on a busy machine with about 3 GB free; earlier runs with the stock models on an idle machine took 0.39 s, 3.5 s and 5.0 s, using about 2.3 GB of RAM for both models.

Getting started is meant to be boring: download the ZIP and double-click start.bat (macOS: start.command; Linux: ./start.sh). On the first start, one button downloads about 4.8 GB, resuming if it stops and checking each file against a pinned SHA-256. I have tested it on Windows 11.

Fine-tuning the judge (Laya)

Out of the box, Laya is a general-purpose judge that hasn't been tuned for couples' chats. So I built a training kit and ran it end to end:

  • Teacher A (DeepSeek-V4.1-Flash) writes 3,000 practice chats to fit chosen answers, then reads each one five times per question without seeing the intended answers; any intended answer the readings don't match is thrown out.
  • Teacher B (Qwen3, a different model family) writes and labels a separate 300-chat practice exam, so the model is never graded by the teacher that taught it.
  • Training runs on Kaggle's free T4 GPUs: three seeds in about an hour, keeping the best on the practice exam. The gates were set before training, among them: macro-F1 up by at least 0.20, no question worse by more than 0.02, and safety recall of at least 90% at the app's 0.8 alarm line.
On the 300-chat practice exam (teacher B: Qwen3-32B) Zero-shot Laya Fine-tuned (v3)
Judgment, macro-F1 over 11 questions 0.33 0.57
Calibration error (ECE) 0.15 0.07
Danger level, mean absolute error (1–10) 2.78 1.56
Safety recall at the 0.8 alarm line 0 of 21 13 of 21 (62%)
False alarms on ordinary chats 2.9% 1.4%

The 95% confidence interval for the F1 gain is 0.18 to 0.31. These are practice-exam numbers; a final, held-back exam comes next. Every question, gate and caveat is in the fine-tuning report.

Two of the gates are still ahead. Safety recall is 62%, against a bar of 90%: five of the eight risky chats it missed were abuse or control read as no risk, such as "You won't see your sister again unless it's through me." And F1 on "is the crisis over?" dipped by 0.08, against a limit of 0.02, on chats where even the teacher split its votes. Still, v3 raises the app's safety notice on 13 of the 21 risky chats instead of 4, so the app has run v3 since 18 October, and the held-back exam decides whether it stays.

The whole run cost about ¥23 (roughly US$3) in DeepSeek API calls and about 9 of Kaggle's 30 free weekly GPU hours. Running the kit end to end also turned up 41 issues in the code and the manuals, including a cache bug that had silently made 20% of the practice chats near-duplicates, fixed before the final training runs.

I trained three versions, v1 to v3, each with a different mix of practice data. What I learned:

  • Compare averages, not the best seed. Seeds of the same version differed by 0.02–0.05 in F1; averaged over their three seeds (leaving out the safety question), v1, v2 and v3 scored 0.57, 0.56 and 0.56.
  • Extra risky chats leak into other answers. Raising their share from 10% to 25% (v2) lifted recall on a separate safety check from 53% to 68%, but the model began calling more chats emergencies and missing a partner who is simply angry. In v3 they train only the safety question: recall on the check (47 fresh risky chats, 19 ordinary) reached 79% with no false alarms, and the side effects mostly went away.
  • You can't measure what your exam teacher can't write. Asked for 25 risky exam chats, Qwen3-14B put a clear signal in only 5; a stricter prompt and the bigger Qwen3-32B got the exam to 21.

Fine-tuning the writer (Gemma)

I also fine-tuned Gemma with the kit's Gemma manual. The app now downloads the fourth of four runs, chosen by a rule written before the results:

  • Data: 1,496 examples. In Chinese, teacher A wrote candidate reply sets to the app's exact prompt, the app's rules dropped bad sets, and the best-scored set became the training reply; the 108 English replies are stock Gemma's own drafts, filtered and scored the same way, so they stay within the app's 60 characters.
  • Training: a 47-minute Kaggle job on a T4 trained a LoRA (rank 16, two epochs) with Unsloth and exported a 3.4 GB Q4_K_M GGUF for the same llama-server.
  • Result: on all 297 practice-exam chats, teacher B (Qwen3-32B) compared it with stock Gemma blind, in both orders: 133 wins, 67 losses, 97 ties (61% with ties counting half; 65% on Chinese chats). It made things up or overpromised once; stock Gemma did 16 times.
  • Still open: on the kit's 50-chat self-check, every gate passes but one: the win rate came in at 0.54 against a bar of 0.55 (a second judging of the same 50 chats gave 0.57).

Dry-running the tester kit with an AI agent

For friends to try the app, I wrote a 15-minute tester kit: six made-up chats, closing questions about price, and a record sheet. An AI agent went through it on my laptop with the real build (fine-tuned Laya, stock Gemma): a dry run, not a user study, with no people and no payments. The flow held up, from an edited reply landing on the clipboard to 我下周六生日哦 ("my birthday is next Saturday") being saved as October 24. It also caught slips: two replies seemed to lose track of who was busy or of the latest topic, and one promised 我下个月肯定能回来 ("I'll definitely be back next month") with nothing in the chat to back it. That kind of overpromise is what the fine-tuned Gemma made once in 297 practice chats, against 16 times for stock Gemma.

Why Does Open Innovation Matter?

  • Where open beat closed, here. Jev Chat Assistant, the app I built on, asks a closed, hosted judge by default and sends it the last ten lines of the chat. I can't tune that model, and its terms don't allow training a model on its answers. Laya's weights are open, so I fine-tuned it for couples' chats against my own bar: macro-F1 0.33 → 0.57, safety recall 0% → 62%. And it runs on the user's own PC, with 0 bytes sent.
  • Privacy you can check. A couple's chat is about as private as data gets, and the other person never agreed to send it anywhere. Open weights are what make this possible: both models run on your machine, the server listens only on 127.0.0.1, and a badge in the header counts the bytes each turn sends off the machine (zero by default). People are wary: 71% of US adults think AI will make their personal information less secure.
  • Costs that allow a one-time price. There is no per-message bill, and the whole fine-tuning run cost about US$3 plus free GPU hours, so the app can be a one-time purchase instead of a subscription.
  • A niche I can tune for. Reading the subtext in Chinese couples' chats (你最好是, "you'd better be"; 没事; 随便, "whatever") is a narrow task. I can train a small judge for it on my own data against my own gates, and when a result surprised me, I could open it up and see why: seed noise, the data mix, the exam teacher.
  • No lock-in. Point the settings at another GGUF file or Ollama model, or at a laya-serve endpoint, and the app uses it.

My Agent Session

Laya-Heart: adding numbers-only Sentry agent tracing claude-opus-5-5
You

@"~\Desktop\Laya-Heart-DEV提交稿-中文版.docx"
帮我把作品改成符合这个要求的样子

<pasted_content id="4834">
Challenge Prompt
Side Hustle
Build something with open-source AI at its core.
That can mean running an open-weight model, building on an open-source agent harness or framework, running inference locally, or all three. Whatever you pick, the open pieces should be what makes your project work.
In your post, tell us why open innovation matters for what you built. Does it run on a laptop with no internet? Keep someone's data off a server they don't control? Let you fine-tune, swap models, or change how your agent behaves? Cost nothing to run? Tell us where your open-based approach worked better than a closed one.
This Week's Theme: Side Hustle
Build something with open-weight models or open-source AI that someone would actually pay for.
That can mean running an open-weight model, building on an open-source agent harness or framework, running inference locally, or all three. Whatever you pick, the open pieces should be what makes your project work.
Think small, specific, and useful. The best builds here solve a real problem for a real person, whether that's a freelancer, a small business owner, a niche hobby community, or your friend who keeps complaining about the same tedious task. A few ideas to get you going:

  • An invoice and receipt sorter for freelancers that runs locally, so client data never leaves their laptop
  • A listing writer for Etsy or eBay sellers that turns a few product photos into a polished description
  • A menu translator for a local restaurant that handles their specific dishes better than a generic tool

In your post, tell us why open innovation matters for what you built. Does running your own model keep costs low enough that the business actually works? Keep your customers' data private? Let you fine-tune for a niche that a general-purpose API handles poorly? Avoid getting locked into someone else's pricing? Tell us where your open-based approach worked better than a closed one.
Bonus points if you land your first user. Get it in front of someone, let them use it, and tell us how it went. Did they pay? Would they? What did they ask for next?
Submission Template
Show your work. We'd love to see how you built it. Save your agent session with DevRelay and embed it in your post, or link to it. It's optional, but it helps the judges understand your process.
Judging Criteria

  • Writing Quality (weighted most heavily)
  • Relevance to the Prompt and Theme
  • Creativity
  • Technical Execution
  • Use of Partner Technology (optional)

Prizes
$250 USD
+ DEV++ Membership
+ Exclusive winner badge
Prize Categories
Featured categories
$200 for each winner
Best Use of Render
Use Render as your project's AI runtime, to host an agent's front end, or to run Hermes or OpenClaw.
Prizes
$200 USD
+ Exclusive winner badge
Best Use of TabPFN
Use TabPFN, Prior Labs' tabular foundation model, to forecast, predict, classify, or spot anomalies from historical data like a CSV. Prior Labs strongly recommends the latest version, TabPFN-3.5: sign up at platform.priorlabs.ai and accept the free non-commercial license to get your API key. Prior Labs favors projects that use TabPFN inside a wider prediction agent (with or without the MCP server).
Prizes
$200 USD
+ Exclusive winner badge
Best Use of Tinker
Use Thinking Machines' Tinker to fine-tune a model for a specific task, and show a clear improvement in performance, latency, or cost over a baseline.
Prizes
$200 USD
+ Exclusive winner badge
Best Use of Arduino
Build with an Arduino UNO Q: run a model on the board, build a physical agent that senses and acts, or optimize a model for it with Qualcomm AI Hub. Open to anyone building with an UNO Q, whether you got one at an in-person Fest or have your own.
Prizes
$200 USD
+ Exclusive winner badge
Best Use of DigitalOcean
Build or deploy your project on DigitalOcean: host your app or agent, run an open-weight model on a GPU Droplet (1-Click Models make this quick), or build an agent on the Gradient AI Platform.
Prizes
$200 USD
+ Exclusive winner badge
Best Use of Gemma
Use Gemma, Google's open-weight model, in building your project: run it locally, fine-tune it, or serve it through Google Cloud or another provider.
Prizes
$200 USD
+ Exclusive winner badge
Partner categories
$100 for each winner
Best Use of Backboard
Build with R-CLI, Backboard's open-source terminal coding agent, compare open-weight models through a single Backboard API key, or give an open-source project's assistant memory and RAG.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of ElevenLabs
Give an open-source agent a voice, transcribe audio for a local model, or generate narration for your demo.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of Entire
Share the agent sessions behind your project in the write-up, or search past agent work to explain why code exists.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of GitHub Copilot
Build with Copilot's coding agent, the Copilot CLI, or the new Copilot app, automate your project with GitHub Actions, or review contributors' PRs with Copilot.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of Mastra
Orchestrate an agent over open models, add memory and tools to a chat agent, or build a workflow that triages GitHub issues.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of MongoDB Atlas
Use Atlas as your data layer: Atlas Vector Search for retrieval, long-term memory for an agent, or the database behind an app built on an open-weight model.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of Sentry Agent Tracing
Show your agent's work: how it's set up, how it performs (latency, tokens, cost), what you found, and how you debugged it. Include traces or screenshots in your write-up.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of SerpApi
Give an agent live web search, ground a RAG app in fresh results, or track news for an open-source project.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of a .tech Domain
Put your project live on a .tech domain you claimed through Hacktoberfest, and link to it in your write-up. Claim a free first-year .tech domain at hacktoberfest.com/my/promos (it renews at $29.99 a year).
Prizes
$100 USD
+ Exclusive winner badge
Best Use of Temporal
Make your agent durable. Wrap it in a Temporal workflow so it survives failures, retries flaky tool calls, and picks up where it left off.
Prizes
$100 USD
+ Exclusive winner badge
Best Use of Tiger Data
Store embeddings with pgvector, run hybrid keyword and vector search for an agent, or let an agent manage a database through Tiger MCP.
</pasted_content id="4834">

这是推荐格式

<pasted_content id="4834">
This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

<!-- What does it do, and who would pay for it? What problem does it solve for them? -->

Demo

<!-- Share a deployed link or a video demo. -->

Code

<!-- Show us the code! You can embed a GitHub repo directly into your post. -->

How I Built It

<!-- Which open-source AI did you use (open-weight models, agent harnesses, frameworks, local inference), and how is your project built around it? -->

Why Does Open Innovation Matter?

<!-- Why does open innovation matter for what you built? What did it make possible that a closed API wouldn't? -->

My Agent Session

<!-- Optional, but judges love it. Save your session with DevRelay and embed it with the agent_session tag (see the challenge page), or link to it. -->

My First User

<!-- Optional, but this is where bonus points live. Did someone use it? Who were they, what did they say, and did they pay? If not, remove this section. -->

Prize Categories

<!-- Which partner categories are you entering? List every one that applies, or remove this section. -->

<!-- Team Submissions: Please pick one member to publish the submission and credit teammates by listing their DEV usernames directly in the body of the post. -->

<!-- Thanks for participating! -->
</pasted_content id="4834">

这个是提交格式
还有我发现我们规划里还有几个赛道的没完成,我们来完成吧?只说重要的对我们有利的信息减少无用信息的输出

Show more
Agent

Research is still running; I'm writing the Sentry tracing module now (it was a "must enter" track in the plan, so it gets built either way; you only need to supply the DSN later).

Agent
▸ Write <project>\laya-heart-web\server\tracing.js
<project>\laya-heart-web\server\tracing.js
File created successfully at: <project>\laya-heart-web\server\tracing.js (file state is current in your context — no need to Read it back)
Agent
▸ Edit <project>\laya-heart-web\server\tracing.js
<project>\laya-heart-web\server\tracing.js
The file <project>\laya-heart-web\server\tracing.js has been updated successfully. (file state is current in your context — no need to Read it back)
Agent
▸ Bash cd "<project>/laya-heart-web" && grep -n "snapshot()\|backends()" server/mock...
$ cd "<project>/laya-heart-web" && grep -n "snapshot()\|backends()" server/mock.js | head; sed -n 1,30p server/http.js | grep -n "import" 
107: snapshot() { 116: backends() { 122: this.notify(this.snapshot()); 127: this.notify(this.snapshot()); 1:// The local web server: the page, a few shared modules it imports, and a small JSON API whose 9:import fs from "node:fs"; 10:import http from "node:http"; 11:import path from "node:path"; 12:import { runTurn } from "../core/turn.js"; 13:import { draftReplies } from "../core/pipeline.js"; 14:import { clearCardCache } from "../core/cards.js"; 15:import { prefixMessages } from "../draft/prompt.js"; 16:import { STAGES } from "../judge/questions.js"; 17:import { CORE_MAX, LAYERS } from "../memory/book.js"; 18:import { ID_RE, PeopleError } from "../memory/people.js"; 19:import { memoryForTurn } from "../memory/turn.js"; 20:import { learnFromChat } from "../memory/learn.js"; 22:export const ROOT = path.resolve(import.meta.dirname, ".."); 24:// Modules the page shares with the server (pure, no Node imports).
Agent

Now wiring it into the server (one import, the two run routes, the memory step, and the engines state the page polls).

Agent
▸ Edit <project>\laya-heart-web\server\http.js
<project>\laya-heart-web\server\http.js
The file <project>\laya-heart-web\server\http.js has been updated successfully.
Agent
▸ Edit <project>\laya-heart-web\server\http.js
<project>\laya-heart-web\server\http.js
The file <project>\laya-heart-web\server\http.js has been updated successfully.
Agent
▸ Edit <project>\laya-heart-web\server\http.js
<project>\laya-heart-web\server\http.js
The file <project>\laya-heart-web\server\http.js has been updated successfully.
Agent
▸ Edit <project>\laya-heart-web\server\http.js
<project>\laya-heart-web\server\http.js
The file <project>\laya-heart-web\server\http.js has been updated successfully.
Agent
▸ Edit <project>\laya-heart-web\server\http.js
<project>\laya-heart-web\server\http.js
The file <project>\laya-heart-web\server\http.js has been updated successfully.
Agent
▸ Edit <project>\laya-heart-web\server\index.js
<project>\laya-heart-web\server\index.js

Related posts