AI 资讯
What I decided about model cost before I had users.
I'm building EverQuill, an AI-powered tabletop RPG platform — a game where an AI acts as your dungeon master, narrating the story and reacting to what you do. Every turn of that story is a message to a language model, and every message costs money. That cost isn't a problem you get to solve later, once you're big. It's a set of choices you make before anyone shows up, because the defaults you pick decide whether a bad day costs you a few cents or a few hundred dollars. Here are three decisions I made before I opened the alpha, and why. 1. When the tier is unclear, I reach for the stronger model This is tier-based routing : which model serves a request depends on who's asking. Better models write better stories and cost more; cheaper ones are faster and rougher. So free players get Claude Haiku (cheaper, ~$0.80 per million input tokens) and paying players get Claude Sonnet (stronger, ~$3.00 per million) — plus, because the platform is designed to route across providers, the same decision could hand a turn to a local model running through Ollama instead of a hosted one. That part is straightforward. The interesting case is the ambiguous one. My routing hands over the cheaper model only when a request is clearly free-tier; everything else — paid, alpha testers, admin, anything the system can't cleanly place as free — gets the stronger model. The instinct most people have is the reverse: default to cheap, protect the margin, don't spend money you don't have to. The reasoning: a rough, disappointing experience for someone who should have gotten a good one is a real cost — it just doesn't show up on the bill. It shows up as someone deciding the product isn't worth paying for. Meanwhile the money risk is capped elsewhere (more on that below), so biasing toward quality can't run away from me. Given a bounded downside on money and an unbounded downside on reputation, I'd rather err toward quality than toward cheap. 2. I stopped paying full price for the part that never chang
AI 资讯
Secrets Management for Test Automation: Handling Credentials Locally and in CI/CD Pipelines
Hard-coding credentials, API keys, or access tokens in automated test suites is one of the most common security risks in software engineering. Ensuring that sensitive variables remain isolated across local developer environments and CI/CD pipelines is critical for keeping your code repositories secure. Here is a practical guide and best-practices workflow for managing secrets cleanly in test automation frameworks. Core Recommendations for Secure Test Suites Zero Source Control Leakage: Always add .env and .env.local files to your .gitignore. Never commit raw tokens or passcodes to git. Use Managed CI Secret Stores : In build pipelines, leverage platform native secret managers such as GitHub Secrets, Harness Secrets, Azure Key Vault, AWS Secrets Manager, or HashiCorp Vault. Dynamic Injection via Environment Variables: Read sensitive data dynamically inside tests using standard environment variables (e.g., process.env.API_KEY or process.env.API_BASE_URL). Enforce Least Privilege: Scope test credentials strictly to non-production environments and configure them to expire periodically. Implementation Examples Local Development Usage: Create a non-committed local environment file (.env.local): API_BASE_URL = https://staging.example.com/api API_TOKEN = your_secret_token_here Execute your test suite while passing or overriding variables inline: # Setting environment variables directly before execution $env :API_BASE_URL = 'https://staging.example.com/api' npm run test :api CI Pipeline Integration (e.g., GitHub Actions): Store API_TOKEN under your repository's Settings > Secrets and variables > Actions, then pass it into your execution job step: - name : Run API Tests run : npm run test:api env : API_TOKEN : ${{ secrets.API_TOKEN }} API_BASE_URL : ${{ secrets.API_BASE_URL }}
AI 资讯
Rendering Custom Fonts to a 2048px PNG with Canvas
A browser preview can look correct while the downloaded image is wrong. The usual failure is timing: CSS eventually applies the custom font to the preview, but Canvas draws once. If the font is not ready at that exact moment, fillText() can silently use a fallback face. The user sees one design and downloads another. I ran into this while building GraffForge, a browser-based graffiti text tool. The free editor compares the same user-entered word across multiple bundled styles, then exports the selected result as a transparent 2048 × 2048 PNG. That gave the export path a clear contract: preserve the exact text; use the selected font; keep spacing, outline, shadow, and skew; fit inside a safe area; preserve real transparency; never upload the user's text or image. Here is the approach that made the output deterministic. 1. Treat export as a separate rendering target Do not enlarge the preview DOM and take a screenshot. Create a fresh Canvas with explicit bitmap dimensions: const EXPORT_SIZE = 2048 ; const canvas = document . createElement ( ' canvas ' ); canvas . width = EXPORT_SIZE ; canvas . height = EXPORT_SIZE ; const context = canvas . getContext ( ' 2d ' ); if ( ! context ) { throw new Error ( ' Canvas rendering is unavailable. ' ); } The width and height attributes define the actual PNG pixel dimensions. CSS sizing and devicePixelRatio are useful for an on-screen preview, but neither should determine the export contract. A fixed bitmap size also makes automated verification straightforward. 2. Load the font before measuring anything Canvas does not redraw automatically when a font finishes loading. Load the exact family, weight, size, and text before calling measureText() : await document . fonts . load ( `400 160px " ${ fontFamily } "` , text ); Passing the actual text is useful because the browser can confirm that the required glyphs are available. After this point, set the Canvas font explicitly: context . font = `400 ${ fontSize } px " ${ fontFamily } "` ;
AI 资讯
Playwright Automation Quick-Start Runbook: Setup, Execution, and Environment Config
Having a clear runbook speeds up onboarding for new team members and provides a standardized execution reference for CI/CD environments. Here is a quick-start automation runbook covering prerequisites, environment variables, execution commands, and output artifacts for a Playwright test suite. Prerequisites Node.js: 24.x (configured via package.json engines) Package Manager: npm Optional: Docker (for containerized pipeline runs) Quick Setup # 1. Install project dependencies cd <repo-root> npm ci # 2. Install Playwright browsers and dependencies npx playwright install --with-deps Execution Commands Full Test Suite: npm test (executes run-all-tests.js) API Suite Only: npm run test:api (executes run-api-tests.js) UI Suite Only: npm run test:ui (executes run-ui-tests.js) Generate Reports: npm run generate:reports Core Environment Variables Configure these keys inside your local .env file or CI secrets: API_BASE_URL — Base endpoint URL for API testing BASE_URL — Target website base URL for UI testing RETRY_COUNT — Maximum retry limit for flaky scenario reruns CUCUMBER_PARALLEL — Number of parallel workers for Cucumber execution Test Artifacts & Outputs Allure Execution Results: reports/allure-results Cucumber JSON Reports: reports/cucumber_report.json Failure Media (Videos/Screenshots): test-results/ (configured via config.js)
AI 资讯
Docker avançado - multi-stage builds, segurança e CI/CD
1. Retomando: da aplicação funcionando ao container pronto para produção Esta série cobriu, até aqui, o suficiente para desenvolver com Docker no dia a dia: conceitos fundamentais, comandos essenciais, Dockerfiles eficientes, rede, volumes e Compose para orquestrar múltiplos serviços. Este último artigo fecha a lacuna entre "funciona no meu Compose local" e "pronto para rodar em produção": imagens menores via multi-stage builds, segurança básica e não negociável, e como tudo isso se integra a um pipeline de CI/CD. 2. O problema que multi-stage builds resolve Compilar ou empacotar uma aplicação frequentemente exige ferramentas que a aplicação não precisa em tempo de execução : compiladores, headers de desenvolvimento, o próprio código-fonte antes de ser transpilado/buildado. Um Dockerfile ingênuo carrega tudo isso para a imagem final: # Ruim: ferramentas de build viajam junto para produção FROM node:20 WORKDIR /app COPY . . RUN npm install && npm run build CMD ["node", "dist/server.js"] Essa imagem inclui o npm , todo o node_modules (incluindo dependências de desenvolvimento), o código-fonte original e as ferramentas de build — frequentemente centenas de MBs de peso morto que nunca são usados depois que npm run build termina, e que ainda aumentam a superfície de ataque da imagem (mais binários, mais coisa que pode ter vulnerabilidade). Multi-stage builds resolvem isso permitindo múltiplos blocos FROM no mesmo Dockerfile, onde estágios posteriores copiam seletivamente apenas o que precisam dos anteriores — o restante do estágio de build simplesmente não existe na imagem final: # Estágio 1: build, com todas as ferramentas necessárias FROM node:20 AS build WORKDIR /app COPY package*.json . RUN npm ci COPY . . RUN npm run build # Estágio 2: produção, só com o resultado do build FROM node:20-slim WORKDIR /app COPY --from=build /app/dist ./dist COPY --from=build /app/node_modules ./node_modules COPY package*.json . CMD ["node", "dist/server.js"] A imagem final não contém o
AI 资讯
Presentation: From Thousands to One: Building LLM-Powered Selection Systems
Jendrik Jördening shares practical engineering strategies for integrating LLMs into production pipelines. He discusses overcoming non-determinism, restricting schemas, separating semantic text extraction from deterministic code, and validating choices using discriminator models. Learn how to structure LLMs with an MVC approach to ensure database integrity, observability, and system reliability. By Jendrik Jördening
AI 资讯
Dog Whisperer
This is a submission for Weekend Challenge: Dog Days Edition Dog Whisperer is an app that looks at a photo of your dog, figures out what it's probably feeling, and then actually says it out loud in a voice that matches the mood. Grumpy dog gets a grumpy voice. Dramatically offended dog gets... a dramatically offended voice. You get the idea. It also doubles as a pet log — meals, weight, and walks tracked over time in Snowflake, with trend charts so you can actually see if your dog's been eating more than usual or losing weight. Add your pets and start logging. Use your unique username to keep track of your pets! Here is App in action: https://dogwhisperer-whi6rye8zklcdtnedyxmww.streamlit.app/ Demo Code Kaku-g / dog_whisperer How I Built It I used Google's Gemini (model: gemini-3.5-flash-lite ) to infer the mood of the dog (or cat, lizard, ferret — whoever's in the photo) from a single image, then passed that straight into Gemini's native TTS (model: gemini-3.1-flash-tts-preview ) to give it a voice that actually matches the mood — a sleepy dog sounds sleepy, a dramatic one sounds dramatic. For logging and trends, I used Snowflake — compute, databases, and tables — to store meals, weight, and walks for every pet and power the trend charts in the app. So it's really two things working hand in hand: a generative AI pipeline paired with a data warehouse. The AI part is what makes the app fun — inferring your pet's mood and giving it a voice. The Snowflake part is what gives it a real use case , since it's something you could keep using for long. Prize Categories I used Google AI and Snowflake, so I'm submitting under both: 🏆 Best Use of Google AI 🏆 Best Use of Snowflake
AI 资讯
Warm Hearth — A Landing Page Built Around One Fire
This is a submission for Frontend Challenge - Comfort Food Edition, Perfect Landing What I Built Warm Hearth — a landing page for a comfort food restaurant built around one idea: everything on the menu comes from the same wood-fired hearth in the back. Instead of treating "comfort food restaurant" as a generic brief, I anchored the whole page to that single hearth: An interactive hearth centerpiece. Right after the hero, there's a hand-drawn CSS/SVG fire pit you can click to "stoke." The flame flares, embers burst upward, and a small honest counter tracks how many times you've stoked it this visit — no fake global numbers, just a real, session-based response to your click. Four dishes, each with real cultural identity. Ramen, warm pies, a cheesy pasta bake, and gulab jamun — each with its own hand-drawn SVG illustration and a border motif pulled from its own cuisine (a jade-and-gold double line for the ramen, a scalloped pastry edge for the pies, an Italian tricolor accent for the pasta, gold paisley tones for the gulab jamun) rather than one generic card style stretched across all four. Living detail, not static photos. Steam rises off the ramen, pies, and pasta bake using the same wisp animation as the hero's hearth, so the whole page reads as one consistent "warmth" language. The gulab jamun gets a syrup shimmer and drip instead, since steam isn't the right detail for a syrup-soaked sweet. Price tags that hang like real kitchen tickets — pinned by a string, swaying gently, and giving a small "flicked" swing on hover instead of sitting flat on the card. Mira, an illustrated host in the corner who offers a rotating table tip when you click her — a small personal touch instead of a static "contact us" widget. Built for actual use, not just to look good in a screenshot: keyboard-focusable tab filters, a skip-to-content link, aria-live regions on the interactive parts, and full prefers-reduced-motion support that disables every animation without breaking the page. Dem
AI 资讯
I spent 11 days optimizing a search ranking that only I could see
This is a submission for DEV's Summer Bug Smash: Smash Stories powered by Sentry . The symptom: good numbers, no users I publish small automation tools on a marketplace. By August I had 23 of them live. Store search looked fine — measured repeatedly, from a real browser, against the real production endpoint: Search term My rank (store UI, Aug 2) sitemap checker #3 google play audit #1 Real numbers after 89 days: 1 active user across all 23 tools. $0 revenue. A #1 ranking and one user is not a rounding error. It is a contradiction, and I spent a week and a half resolving it in the wrong direction. Eleven days of correct answers to the wrong question If ranking is fine and users are zero, the fault must be downstream — that was the reasoning. So I went looking for it, carefully: Demand analysis. Pulled 3,655 listings, then went deeper to 12,834 to check for sampling bias in the first pass. (There was one. I found it and corrected it.) Naming analysis. Split the corpus by whether the title contained a well-known platform name. Median users: 5 vs 2. Age-cohort analysis. Measured the base rate for new listings: only 11% (n=9) get their first user within 0–3 days of publishing, against 74% at 14–30 days. Mine were young. The zeros were, statistically, unremarkable. Acted on all of it. Renamed 5 tools. Added output schemas across the board — the platform's own quality score went from 74 to 78–79. Every one of those produced a defensible number. Not one of them changed anything. That pattern is the actual signal, and I missed it for too long: when every hypothesis confirms and nothing moves, stop testing hypotheses and start testing the instrument. "It reproduced" is not "it's correct" I had re-measured the ranking several times over those days. Same answer each time. I read that as confirmation. It isn't. Re-running a measurement under identical conditions reproduces the same bias just as faithfully as it reproduces the same truth . Repetition rules out transient noise and
AI 资讯
My security hook silently stopped guarding. The bug was one line of encoding.
This is a submission for DEV's Summer Bug Smash: Clear the Lineup powered by Sentry . Project Overview I run a set of local policy guards around an AI coding agent. They are ordinary PreToolUse hooks: before the agent is allowed to perform an action, the proposed tool call is handed to a small Python script as JSON on stdin . The contract is two exit codes. exit 0 → allow exit 2 → block, and send the reason back to the agent as feedback There are several. One refuses access to credential paths. One intercepts destructive shell commands. One enforces a directory boundary. And one — malformed-read-guard.py — blocks the agent from reading files that contain corrupted tool-call syntax, because reading that syntax makes the model start emitting it too, and the session locks up. They had been working for weeks. One of them had also, for some of that time, been doing nothing at all. Bug Fix or Performance Improvement The symptom Same file. Same bytes. Two locations. Placed at an ASCII path → guard fires, exit 2 , read blocked. Placed under a directory whose name contains Japanese characters → exit 0 , read allowed. No exception. No stack trace. No log line. Nothing anywhere said a decision had been skipped. The hook ran, the hook returned "allow", and the agent read a file it was supposed to be protected from. The mechanism Three steps, and the ugly part is that each one is individually defensible. 1. The payload is UTF-8. The reader is not. Hook input is always UTF-8. But on Windows, Python opens sys.stdin using the locale encoding — on this machine, cp932 . So this line data = json . load ( sys . stdin ) decodes UTF-8 bytes as cp932. 2. Mojibake does not raise. That is the whole problem. cp932 is permissive enough that UTF-8 bytes map onto some sequence of characters. You do not get a UnicodeDecodeError you can catch and log. You get a string that is merely wrong, and it flows onward as valid data: 'C:\\...\\self-catering\\_\udc85部\\再開メモ.md' ← what the guard actually rec
AI 资讯
Reading .xlsx in the browser without a spreadsheet library
I run a small site that converts bank CSV exports into the file format QuickBooks Desktop accepts. The whole thing runs client-side, and the privacy claim it makes is unusually literal: every page ships a Content Security Policy with connect-src 'none' , so the browser refuses to let the page make any network request at all. Open the Network tab while you convert a file and it stays empty. That's the feature, not a nice-to-have on top of it. When I added Excel support last week, the obvious choice was SheetJS. I decided against it, and the reason wasn't bundle size. The claim I want to be able to make is "your file never leaves the browser, and here is the policy that enforces it." Pulling in a large third-party parser turns that into "…and also trust this dependency," which is a materially weaker claim for a tool that handles people's bank statements. So I wanted to find out how much of the format I actually needed. Less than I expected. An .xlsx file is a ZIP archive containing XML: xl/workbook.xml lists the sheets, xl/worksheets/sheet1.xml holds the cells, xl/sharedStrings.xml is a deduplicated string pool that cells reference by index, and xl/styles.xml carries the number formats. Reading that needs three capabilities, and the browser already provides two of them. Unzipping means walking the ZIP central directory, which is about forty lines. Decompression is DecompressionStream('deflate-raw') , which is native. For the XML I hand-rolled a tag scanner rather than reaching for DOMParser , because my tests run in Node where DOMParser doesn't exist, and I would rather have one code path than two. Dates The part that took the most care was dates, because Excel doesn't store them as dates. A cell holding 5 January 2024 contains the number 45296 , and whether that number should be displayed as a date depends on the cell's number format — which lives in a different file inside the archive. So the parser has to read styles.xml , work out which style indexes correspond to
AI 资讯
Building "112 for Dogs": How I Combined Gemini AI, Solana, and Voice Agents to Save Strays
This is a submission for Weekend Challenge: Dog Days Edition What I Built PawID & Care is an enterprise-grade, multi-role emergency response and biometric identification platform built for community animal welfare and stray management. Operating as a "112-for-dogs" system, the platform bridges cutting-edge artificial intelligence, distributed ledgers, enterprise analytics, and autonomous voice agents into a single unified workflow. The core goal is to solve the fragmentation in animal rescue: pet owners lose dogs, street animal injuries go unreported, and cities lack real-time community health tracking. PawID & Care provides instant biometric triage, role-based portals for Owners, Rescuers, and Vets, and real-time emergency voice dispatching. Demo 📹 Watch the Demo Video: https://youtu.be/_B-2dL2fDX4 Code GitHub Repository: [ https://github.com/GamersStop/paw-id-care ] bash # Clone the repository git clone [https://github.com/GamersStop/paw-id-care](https://github.com/GamersStop/paw-id-care) # Install dependencies npm install # Start the server npm start
AI 资讯
web page hosting
How to Host a Website Using GitLab Pages If you have a website made with HTML and CSS, you can host it for free using GitLab Pages . GitLab Pages takes the files from your GitLab repository and publishes them as a website. For this, you need to create a .gitlab-ci.yml file. This file tells GitLab how to deploy your website. After pushing the file to your repository, GitLab creates a pipeline. When the pipeline finishes successfully, GitLab Pages gives you a URL which you can open in a browser to see your live website. Understanding the Pipeline A pipeline is the process GitLab uses to run the instructions written in .gitlab-ci.yml . If the pipeline fails, the website will not be deployed correctly. Sometimes the pipeline can fail because of an invalid YAML file, incorrect indentation, or a problem in the deployment commands. Another common problem is trying to create a public folder when the folder already exists. For a simple HTML and CSS website, the important thing is that the public folder contains your website files and index.html should be directly inside it. For example, the structure should look like this: public/ ├── index.html ├── style.css └── images/ The index.html file is important because it is the main page GitLab Pages looks for when someone opens the website. Hosting More Than One Website You can host multiple websites using GitLab Pages, but if the websites are completely different projects, it is better to create a separate GitLab project for each website . For example, you can have one project called youtube-clone and another project called portfolio . Each project can have its own HTML, CSS, .gitlab-ci.yml , pipeline and Pages deployment. This makes the projects easier to manage and prevents one website from affecting another website. So, GitLab is not only a place to store your code. With GitLab Pages and CI/CD pipelines, you can also use it to turn your HTML and CSS project into a live website that can be accessed through the internet.
AI 资讯
Caninography
This is a submission for Weekend Challenge: Dog Days Edition I built Caninography, a small digital archive for exploring dog breeds from around the world. I wanted it to feel more like a digital museum than a normal dog website. You can explore breeds, their origins, history, countries, characteristics and connections between them. The whole design is dark, clean and visual. I also kept it silent, so there is no distracting audio or player UI. Live Demo: canonigraphy.vercel.app GitHub: https://github.com/maisamabbas0323/canonigraphy.git How I Built It I built it with React, Vite and TypeScript. For the visual side, I focused a lot on typography, photography, smooth transitions and responsive layouts. I also added an interactive world atlas and a constellation-style view to explore breed relationships. For the content, I used Google Gemini to help create short and interesting breed information. I didn't wanted to make another chatbot. Instead, Gemini stays behind the experience and helps make the archive content more rich while people simply explore it. Prize Categories Best Use of Google AI Caninography is submitted for Best Use of Google AI. I used Google Gemini to generate concise, breed-specific information based on the archive data. The idea was to use AI in the background, not put a chatbot in front of the user. Built With React Vite TypeScript Google Gemini CSS SVG Canvas A Little About The Idea I always felt dog breed information is mostly shown as simple lists. So I thought: What if a dog archive felt like a museum? That small idea became Caninography. A place to explore their stories, origins and history — one breed at a time.
AI 资讯
I verified 51 sets of US tax rules by hand and turned them into a static site
Run the same salary through three different paycheck calculators and you'll get three different answers. None of them explain why. That bothered me enough to spend three weeks building an alternative. The result is payculate.org — a paycheck calculator for all 50 US states and DC where every deduction line opens up and shows its own arithmetic . The interesting problem wasn't the code The tax math itself is straightforward: progressive brackets are a loop, FICA is two multiplications with a cap. I had a working federal calculator in an afternoon. The hard part was that every state is a special case , and a generic model breaks on most of them: Wisconsin has a standard deduction that shrinks as you earn more — it starts at $13,230 and falls by 12 cents per dollar above a threshold, reaching zero around $126,000. Alabama lets you deduct your entire federal income tax before calculating state tax. The more federal tax you pay, the less Alabama income you have. Utah looks flat at 4.5%, but gives a taxpayer credit that phases out with income — so the effective rate climbs while the headline rate never moves. Ohio taxes nothing on the first $26,050, then a flat 2.75%. South Carolina rewrote its entire income tax in March 2026: six brackets became two (1.99% / 5.21%), and the federal standard deduction was replaced by a state-specific deduction that phases out above $40,000 of AGI. That last one I only caught during a routine data check last week. Most calculators I checked are still showing the old six-bracket system. The lines nobody counts The bigger discovery was what national calculators leave out entirely: employee-paid state payroll premiums . Washington charges no income tax at all. But Paid Family & Medical Leave (0.807%) and WA Cares (0.58%, uncapped) still take about $1,040 a year from a $75,000 salary . Most tools show $0 on that line. California's SDI lost its wage cap in 2024 and now takes 1.3% of every dollar — on a $200,000 salary that's $2,600 that appears
AI 资讯
How Do I Send Password Reset Emails from a Backend App Using an Email API?
Here's the full flow the way I've built it, using Notify as the email API. The shape of this is the same regardless of which provider you pick — generate a token, send a link, verify it on submit — so most of this applies no matter what you're using; I'll flag the one part that's specific to Notify. The Flow, End to End User requests a password reset Your backend generates a secure, short-lived reset token Your backend stores a hashed version of that token Your backend sends an email with the reset link, through an email API User clicks the link and submits a new password Your backend verifies the token, updates the password, and invalidates the token Step 1: Generate the Reset Token Use a cryptographically secure random value, not anything guessable, and store only a hashed version in your database — if your database ever leaks, the raw tokens aren't exposed alongside it: const crypto = require ( ' crypto ' ); function generateResetToken () { const token = crypto . randomBytes ( 32 ). toString ( ' hex ' ); const tokenHash = crypto . createHash ( ' sha256 ' ). update ( token ). digest ( ' hex ' ); return { token , tokenHash }; } Give it a short expiration — 15 to 60 minutes is typical. Step 2: Build the Reset URL https://yourapp.com/reset-password?token=RESET_TOKEN The token goes in the link the user clicks; the hash is what you store and check against later. Step 3: Send the Email This is the Notify-specific part. There's no SDK to install — it's a single HTTP request with your API key in the header: async function requestPasswordReset ( email ) { const user = await findUserByEmail ( email ); // Don't reveal whether the email exists if ( ! user ) return ; const { token , tokenHash } = generateResetToken (); const expiresAt = new Date ( Date . now () + 1000 * 60 * 30 ); // 30 minutes await saveResetToken ( user . id , tokenHash , expiresAt ); const resetLink = `https://yourapp.com/reset-password?token= ${ token } ` ; await fetch ( ' https://notify.cx/api/email/send
AI 资讯
🐾 PawSafe: An AI-Powered Food Safety Checker for Dogs
This is a submission for Weekend Challenge: Dog Days Edition What I Built PawSafe is an AI-powered web application that helps dog owners answer a simple but important question: "Can my dog eat this?" Users can enter the name of a food, upload a photo, or provide both. PawSafe then analyzes the information using Google's Gemini API and provides a simple safety assessment. The result is categorized into four levels: 🟢 Generally Safe 🟡 Use Caution 🔴 Not Safe ⚪ Unable to Determine Along with the result, PawSafe provides explanations, potential warnings, and safer alternatives when appropriate. My goal was to build something that was useful, simple to understand, and approachable for dog owners rather than making users search through multiple sources every time they encounter an unfamiliar food. Demo Live Demo Code GitHub Repository How I Built It PawSafe is a full-stack application built with: Frontend React Vite Tailwind CSS Lucide React Backend Node.js Express Multer CORS Google Gemini API Deployment Render GitHub The basic flow looks like this: User ↓ Food name / Image / Both ↓ React Frontend ↓ Express API ↓ Google Gemini ↓ Structured Analysis ↓ PawSafe Result Card One of the main technical decisions I made was to keep the Gemini API integration on the backend rather than exposing the API key in the frontend. The frontend sends the user's food information to the Express API. The backend then communicates with Gemini and returns the structured analysis to the frontend. I also wanted the application to support both text and images independently, while still allowing users to provide both when additional context is useful. Prize Categories Best Use of Google AI PawSafe is submitted for the Best Use of Google AI prize category. Google's Gemini API is the core intelligence behind the application. It is used to analyze both text-based and image-based food information and generate a structured safety assessment. The AI response is then presented through PawSafe's interface
AI 资讯
I run a surf forecast for 20 breaks in Morocco on EUR 0/month. Here's the stack.
I live on the Taghazout coast in Morocco - a strip of Atlantic between Agadir and Imsouane that's basically one long right-hand point break after another. Two years ago the only way to know if tomorrow was worth it was to check three different global forecast sites, none of which knew the difference between Anchor Point and the beach break 400m south of it. So I built taghazout.io . It now covers 20 named breaks, runs in 10 languages, and costs me nothing per month. Here's how it's actually put together - including the parts I'd do differently. The stack is deliberately boring Hand-rolled PHP. No framework, no build step, no node_modules. About 4,800 files, server-rendered, no hydration. That sounds like a confession, but it was the right call for one reason: my readers are on phones, on cafe Wi-Fi, often on 3G. A server-rendered page that ships HTML and a little CSS beats anything I could have built with a client-side framework in that environment. Time-to-content is the only metric that matters when someone is standing on the beach deciding whether to paddle out. The hosting is a cheap shared plan. The forecast data is free and open. The whole thing runs at EUR 0/month recurring , which was a hard constraint from day one. The interesting part: two ocean models that disagree The forecast blends two sources: Open-Meteo (CC BY 4.0) - the primary, with a marine endpoint that covers our coastal cells. NOAA WaveWatch III via PacIOOS - the second opinion. Here's the thing nobody tells you: they disagree, a lot. On the same hour at the same break I've seen WaveWatch read ~55% higher than Open-Meteo (1.36m vs 0.88m). Offshore models resolve coastal bathymetry badly, and our points are exactly the kind of close-in, shallow-reef setups where that bias shows up. The wrong fix is to pick one and pretend. What I did instead: Run both, cache both. Compute agreement over a 72-hour window - a Pearson correlation on the swell rhythm plus a circular difference on direction (you can'
AI 资讯
One terminal, two trust levels — running Claude Code against a real subscription and a cheap proxy
Part of an ongoing series on model routing and trust tiering for agentic coding tools. This one's the boring, working half — no bug hunt, just a setup that's been running clean across two machines. The problem Claude Code does one thing well: careful, scoped edits with a real plan-then-execute loop behind them, backed by a subscription you're already paying for. Not every task needs that. Exploratory reads, "summarize this directory," draft-and-discard scratch work — most of that doesn't need the most capable model watching every token. The fix is a second, cheaper backend for that category of work. The catch: Claude Code only speaks Anthropic's Messages API. It has no built-in notion of "same tool, different model." So the question is how to point it somewhere else without giving up the interface. The stack Trusted agent: claude — real Anthropic subscription, default session Cheap agent: claude-cheap — same CLI, routed through a self-hosted proxy Proxy: LiteLLM, translating Anthropic-format requests to DeepSeek V4 (pro for Sonnet-tier calls, flash for Haiku-tier) served through an OpenRouter API Transport: a persistent SSH tunnel from a small VPS back to each machine The proxy itself wasn't new. It's the same LiteLLM instance already routing a separate content pipeline I run. The actual work here was wiring Claude Code to it: a shell function and a few environment variables. The core trick and it took me a few week to learn this is to point ANTHROPIC_BASE_URL at LiteLLM's /v1/messages endpoint, not the OpenAI-compatible path LiteLLM also exposes. Claude Code only understands the Anthropic shape, so the OpenAI-shaped endpoint fails in ways that look like a client bug and aren't. Once LiteLLM sits on the right endpoint and translates underneath, Claude Code has no idea it isn't talking to Anthropic. The one bug worth flagging Claude Code's Plan Mode attaches a context_management parameter to its requests. Anthropic's API handles it. Most other backends don't recogniz
AI 资讯
😸Catbot Integration, AI Office, Cat Mode (AI Avatar v17: VS Code and Chrome Extension)
Intro AI Avatar is a free app where your VRoid (VRM) avatar cheers you with all its might .🤗 It lives in your VS Code sidebar (reacts to Claude Code / GitHub Copilot) or browser side panel (reacts to ChatGPT / Claude). Animations and speech bubbles all run without AI too. This time I have three main topics. 🤝Catbot Integration 🏢AI Office 😺Cat Mode Let's see how they are! Catbot Integration I was asked to collaborate with my DEV Community friend @annavi11arrea1 Catbot . Catbot is A galactic robot cat you can talk to from any device — and a harness that lets you switch between (or combine) all of your AI models. https://github.com/AnnaVi11arrea1/catbot I was happy about this offer because I loved Anna's creativity and cool designs. I added the features below to AI Avatar to integrate Catbot. Launch Cat button: With this button, AI Avatar can run Catbot. Catbot with button: This makes Catbot stay beside AI Avatar. Cat Boss button: This changes the AI Office boss from a VRM avatar to Catbot. Cat Mode Many people feel that animals are healing and soothing. It is close to the AI Avatar concept of cheering people up. So I decided to add Cat Mode . I added the features below to make it look like a cat. Cat-like text, "Meow/Purrr" in English and "にゃ~" in Japanese Cat emojis Cat pose animations A new avatar with cat ears and cat whiskers. To tell the truth, the hardest part of making this mode was adding whiskers to the avatar using Blender . I can do basic things in Blender, but it is too difficult for me, even with the help of AI, just to add whiskers. It would be more fun if I added other animal modes too. AI Office AI Avatar displayed only one avatar. I thought it could do more things if it displayed several avatars at once. So I added AI Office mode. Two avatars are displayed and talk and move around when idle, and they also make a communication animation when using AI or clicking. I made one avatar a boss and one a worker. The hard part of making this mode was the timin