AI 资讯
Building an AI Pharmacist: Detecting Drug-Drug Interactions with RAG and OCR
Ever looked at a pile of medicine bottles and wondered, "Is it actually safe to take these together?" Polypharmacy—the simultaneous use of multiple drugs—is a significant challenge in modern healthcare. Misunderstanding Drug-Drug Interactions (DDI) can lead to severe side effects or reduced efficacy. In this tutorial, we are building an AI Pharmacist Assistant , an automated engine that uses Optical Character Recognition (OCR) to scan drug labels and Retrieval-Augmented Generation (RAG) to cross-reference a drug database. By leveraging AI healthcare automation and sophisticated LLM reasoning , we can create a safety net that identifies potential contraindications in seconds. The Architecture 🏗️ The system follows a linear pipeline: capturing raw image data, converting it to structured text, retrieving medical facts from a local SQLite-based knowledge base, and finally, using an LLM to reason about the interactions. graph TD A[Drug Packaging Image] -->|Tesseract OCR| B(Extract Drug Names) B --> C{Search SQLite DB} C -->|Found Interaction Data| D[Context Construction] D --> E[LLM Reasoning Engine] E --> F[Safety Report & Warnings] C -->|Not Found| G[Web Search/LLM General Knowledge] G --> E Prerequisites 🛠️ To follow along, you'll need the following tech stack: Python 3.10+ Tesseract OCR : For extracting text from images. SQLite : To store our curated DrugBank-style interaction data. RAG Pattern : To provide the LLM with ground-truth medical data. OpenAI SDK : For the final reasoning step. Step 1: Extracting Labels with OCR 📸 First, we need to turn those pixels into text. We use pytesseract to handle the OCR process. import pytesseract from PIL import Image def extract_drug_names ( image_path ): # Pre-processing could be added here (grayscale, thresholding) text = pytesseract . image_to_string ( Image . open ( image_path )) # In a real scenario, use an LLM or Regex to pull specific # active ingredients from the raw text print ( f " Detected Text: { text } " ) return t
开发者
Automatiza tareas repetitivas con un bot en Python
Cada tarea manual y repetitiva que haces cada semana es tiempo (y dinero) que un script de Python puede recuperarte. La automatización no es solo para grandes empresas. Primero: ¿qué vale la pena automatizar? Busca tareas repetitivas, basadas en reglas y frecuentes . Algunos ejemplos habituales: Descargar y consolidar informes cada mañana. Copiar datos entre una web y una hoja de cálculo. Enviar recordatorios o alertas. Vigilar precios o cambios en una página. Una regla práctica: si puedes explicar la tarea como una lista de pasos sin excepciones, probablemente se puede automatizar. Las herramientas del ecosistema Python requests / httpx para hablar con APIs y webs. BeautifulSoup / Playwright para web scraping (Playwright cuando la página carga con JavaScript). pandas para transformar datos. APScheduler o cron para ejecutarlo en un horario. Bots de Telegram o Discord para recibir avisos donde ya estás. Un ejemplo mínimo Vigilar el título de una página y avisar si cambia: import requests from bs4 import BeautifulSoup def titulo ( url ): html = requests . get ( url , timeout = 10 ). text return BeautifulSoup ( html , " html.parser " ). title . string . strip () anterior = titulo ( " https://example.com " ) # ...ejecutado por cron cada hora... actual = titulo ( " https://example.com " ) if actual != anterior : print ( " ¡Cambió! " ) # aquí enviarías un mensaje de Telegram De script a bot fiable Un script que corre en tu portátil es un buen comienzo, pero un bot fiable vive en un servidor, registra lo que hace, maneja errores (reintentos, tiempos de espera) y te avisa si algo falla. Ese salto —de experimento a herramienta en la que confías— es donde más aporta un desarrollador. ¿Tienes una tarea que odias hacer a mano? Probablemente se pueda automatizar. Cuéntame cuál es y te digo cómo abordarla: contacto .
开源项目
🔥 DrewThomasson / ebook2audiobook - Generate audiobooks from e-books, voice cloning & 1158+ lang
GitHub热门项目 | Generate audiobooks from e-books, voice cloning & 1158+ languages! | Stars: 19,882 | 141 stars today | 语言: Python
开源项目
🔥 youssofal / MTPLX - 3x faster speeds on MLX | Qwen 3.8 27B | Native MTP Speculat
GitHub热门项目 | 3x faster speeds on MLX | Qwen 3.8 27B | Native MTP Speculative Decoding On Apple Silicon With No External Drafter. | Stars: 1,421 | 44 stars today | 语言: Python
开源项目
🔥 marceloprates / prettymaps - Draw pretty maps from OpenStreetMap data! Built with osmnx +
GitHub热门项目 | Draw pretty maps from OpenStreetMap data! Built with osmnx +matplotlib + shapely | Stars: 12,882 | 58 stars today | 语言: Python
AI 资讯
GitHub API Rate Limits: an Unauthenticated 304 Still Costs You a Request
No token. One IP. July 29, 2026: GET /repos/python/cpython 200 5996 B remaining 32 -> 31 + If-None-Match (no Authorization header) 304 0 B remaining 31 -> 30 + If-None-Match 304 0 B remaining 30 -> 29 + If-None-Match 304 0 B remaining 29 -> 28 Three conditional requests. Three 304 Not Modified . Zero bytes of body across all three. Three requests gone from a bucket of 60 per hour. I opened the terminal to write the opposite post. The short version: if you call the GitHub REST API without an Authorization header, an If-None-Match request that comes back 304 still decrements x-ratelimit-remaining . The ETag saves you bytes. It does not save you quota. GitHub's documentation states the claim five times on one page and attaches the condition to two of them, and that clause falls off easily when a sentence gets quoted on its own. The post I meant to write My working title was something like "poll GitHub for free with ETags". I believed it. I had read the sentence about 304 responses not using your rate limit, I had repeated it to other people, and the plan was a tidy little piece with a before-and-after budget chart. The first run killed it. remaining went down. My first reaction was that my counter reading was wrong, which is the normal reaction and usually the correct one. It was not wrong. So the post changed, and the finding turned out to be worth more than the one I went in with. Does a 304 count against the GitHub rate limit? What the docs actually say Here is the part that matters, and I want to be precise because it would be easy and dishonest to turn this into "GitHub's docs are wrong". They are not. On the page Best practices for using the REST API the claim shows up five times. Two of the five carry a condition; three do not. Here is the strict one, the only place on the page where the condition is spelled out as a header: "Making a conditional request does not count against your primary rate limit if a 304 response is returned and the request was made while c
AI 资讯
Purged and Embargoed Cross-Validation for Options ML
Why plain k-fold silently overfits your trading model — and the 4-line fix that stops it. The Problem With k-Fold in Time Series Financial data is sequential. k-fold shuffles rows, so a training row from 2 PM Tuesday sits next to a test row from 10 AM Monday. Worse: triple-barrier labels overlap . A label at bar t looks 6 bars into the future; a training row at t+2 "knows" part of that future. The model leaks. V1's history is full of "HIGH overfit" verdicts — train AUC high, test AUC flat. Plain TimeSeriesSplit is only marginally better; it still lets adjacent windows bleed into each other. Purged + Embargoed CV For each test window [t0, t1] : Purge any train row whose label window overlaps the test window. Embargo max_training_horizon bars after the test window — drop those too. Overlapping labels are not i.i.d. Purging + embargoing makes the split honest. def purged_embargo_split ( n , n_splits = 5 , embargo_frac = 0.02 ): idx = np . arange ( n ) fold = np . array_split ( idx , n_splits ) splits = [] for i in range ( n_splits ): test = fold [ i ] emb = int ( len ( test ) * embargo_frac ) lo , hi = max ( 0 , test [ 0 ] - emb ), min ( n , test [ - 1 ] + emb + 1 ) train_mask = np . ones ( n , bool ); train_mask [ lo : hi ] = False splits . append (( idx [ train_mask ], test )) return splits Tune Only When You Have Enough Optuna once "won" a validation set with only 4 decisive rows — statistically meaningless. Rule: never tune when the decisive (non-abstained) validation rows are below ~30–50. Widen the date range or symbol basket first; don't trust the trial. Three-Way Split, Always train (fit) → validation (early stop + HP select) → disjoint calibration set (sigmoid/ isotonic) → test (untouched, final score only). V1 sometimes conflated validation and calibration. Keep them separate. The Promotion Gate Log every trial's train/val/test gap, not just the winner's test score. Promote only if replay AND shadow (≥1 live session) both beat baseline on buyer metrics : 1.5x
AI 资讯
Options Buyer ML: Why One Model Fails (and the V2 Fix)
Lessons from a real rebuild of an options-buyer prediction system. No profit claims — just the architecture that fixes the chronic bugs of V1. The Core Mistake in V1 V1 asked one XGBoost model one big fuzzy question: "CE ya PE?" — directly from raw CE/PE premium data. Premium is a transformed signal (underlying move × delta × gamma × IV × theta × spread × strike distance × liquidity). The model learned noise as much as signal. Concrete evidence from the research logs: Balanced accuracy stuck at 51–61% for months — hyperparameters were never tuned ( lr=0.02, depth=3 defaults used throughout; Optuna existed but was never run). A partition bug ( iv_change_1d shift inside single-row groups) silently zeroed a whole feature for the entire history. A rollup config flag compressed 15-minute bars into 1 row/day, destroying 760× of training volume (387 sequences instead of 295K+). Live paper trading: 31.6% win rate, −₹90.3k PnL , entry confidences only 55–64%. V2 Principle: Split the Question underlying mechanics --> side, range, ETA, invalidation option chain scanner --> is the buyer contract worth paying for? XGBoost (many heads) --> thin calibrated learner on clean mechanics Rule: underlying decides side; option contract decides execution eligibility. CE/PE premium is validated against, never learned as, direction. Many Shallow Heads, Not One Deep Model Instead of one CE/PE answer, V2 trains separate narrow heads: underlying_up/down_touch_{15,30,60}m ce_1p3x / ce_1p5x / ce_2p0x and pe_1p3x / pe_1p5x / pe_2p0x (SEPARATE CE and PE) no_trade_quality This single change removes most of the CE/PE confusion V1 fought for months. The Shallow Regularized Grid (the actual fix for overfit) learning_rate = 0.015 – 0.035 n_estimators = 800 – 2000 ( early stop ) max_depth = 2 – 3 min_child_weight = 12 – 40 gamma = 0.1 – 2.0 subsample = 0.65 – 0.90 colsample_bytree = 0.55 – 0.85 reg_alpha = 0.5 – 3.0 reg_lambda = 6.0 – 20.0 scale_pos_weight = min ( neg / pos , 8.0 ) V1's intraday head ha
AI 资讯
Python Developer Interview Preparation: What to Practice Beyond Coding
Preparing for a Python developer interview often starts with coding problems. You practice arrays, strings, dictionaries, functions, and algorithms. Then you solve a few more problems and feel like you're ready. But an actual Python developer interview can test much more than whether you can write working code. You may need to explain your decisions, debug an unfamiliar piece of code, discuss Python concepts, or describe how you would approach a real development problem. Here are the areas I'd focus on before an interview. 1.Don't Just Solve Python Problems—Explain Them It's possible to solve a coding problem correctly and still struggle in an interview. Interviewers often want to know: Why did you choose this approach? What is the time complexity? What happens with edge cases? Is there another way to solve it ? How would you improve the solution? Try explaining your solution aloud after solving it. If you can't explain why your code works, you probably don't understand the solution as well as you think. 2. Know Python Beyond the Basics Don't stop at syntax. Review concepts such as: Lists, tuples, sets, and dictionaries Mutable vs immutable objects *args and **kwargs Exception handling Iterators and generators Decorators List comprehensions Context managers Object-oriented programming Memory management You don't need to memorize every Python feature. Focus on understanding concepts well enough to explain when and why you'd use them. 3. Practice Debugging Real developers don't spend all day writing code from scratch. A large part of the job involves understanding existing code and fixing problems. Take a small Python program with a bug and practice: Reproducing the problem. Reading the error carefully. Finding the likely cause. Testing your assumption. Fixing the issue. Explaining why it happened. This is also useful interview practice because debugging reveals how you think when the answer isn't immediately obvious. 4.Be Ready for Real-World Questions Depending on t
AI 资讯
I Tested 5 AI Engines On My Own Sites. None Agreed.
I Tested 5 AI Engines On My Own Sites. None Agreed. In July I wrote that my open-source...
AI 资讯
Python Polars Cheat Sheet: Fast DataFrames for Busy Engineers
Polars hits the sweet spot between Pandas’ ease and Spark’s scale. If you’ve ever waited on a groupby or cursed a memory error, this cheat sheet is for you. I’ve pulled the patterns that save time in real pipelines, not just toy examples. Bookmark this before your next ETL run. Setup and Basics First, get Polars and a dataset. The lazy API is the default now, so you’ll rarely need to call .lazy() explicitly. Start with a CSV or Parquet file, or create a DataFrame from scratch. pip install polars pyarrow import polars as pl df = pl.read_csv('data.csv') # or pl.read_parquet() df = pl.DataFrame({'a': [1, 2], 'b': ['x', 'y']}) Selecting and Filtering Polars uses expressions, not strings. This feels odd at first but pays off when you chain operations. The syntax is consistent: every column is an expression you can transform, filter, or aggregate. df.select(['a', 'b']) # columns by name df.select(pl.col('a').alias('renamed')) df.filter(pl.col('a') > 10) df.filter(pl.col('b').is_in(['x', 'z'])) df.filter(pl.col('a').is_null()) Transforming Data Polars expressions are composable. You can nest them, reuse them, and even store them in variables. This is where the library shines over Pandas. df.with_columns(pl.col('a').cast(pl.Float64)) df.with_columns(pl.col('a').fill_null(0)) df.with_columns((pl.col('a') * 2).alias('a_doubled')) df.with_columns(pl.col('b').str.to_uppercase()) df.with_columns(pl.col('a').is_between(10, 20)) Grouping and Aggregations Groupbys in Polars are lazy by default. This means you can stack multiple aggregations without materializing intermediate results. The syntax is clean, but watch out for the order of operations. df.group_by('b').agg(pl.col('a').sum()) df.group_by('b').agg([pl.col('a').mean(), pl.col('a').max()]) df.group_by('b').agg(pl.col('a').quantile(0.9)) df.group_by_dynamic('timestamp', every='1d').agg(pl.col('a').sum()) Joins and Concatenation Joins in Polars are explicit. You’ll specify the join type and the columns to join on. Concatenatio
AI 资讯
A Dead PID Held My Lock for 2 Hours: One Missing Line, Zero Output, exit 0 Every Time
For 30 straight days as a college student earning ¥100k/month, I posted to Instagram by hand, and then I burned out and stopped. Today the same job runs on a Claude Code autonomous environment, I touch nothing, and it holds up ¥1.2M/month in revenue. Except for the two hours when it quietly stopped: three consecutive launchd runs, zero pieces of content generated, last exit=0 every single time, and not one alert. The cause was a process that had already been killed, holding a lock file nobody would take away from it. Why this setup works From "doing the work" to "building the environment" The problem with updating social media by hand is that it burns willpower. No matter how motivated you are, sleep, health, and mood all fluctuate. During the period when I was laid off and my income went to zero, I had no mental slack for posting at all. The autonomous environment I spent six months building with Claude Code runs regardless of my emotional state. launchd calls a script, the script generates content with claude -p (MAX plan quota; paid APIs are off-limits), the output is queued for auto-posting, and it goes out to Instagram every day at 19:30. As long as this machinery keeps working, ¥1.2M/month in sales holds up without me lifting a finger. The mental model I want to hand you A lot of people think "automation = writing scripts," and that's only half right. A script is correct at the moment you write it. Given time, external dependencies break, processes die for reasons you didn't anticipate, and lock files turn into debris that blocks every future run. An autonomous environment that actually works is one that assumes breakage and carries a layer that repairs it. The lock story here is a textbook case. ~/dev/brand-404/sns/gen_feature.py is a script launched on a schedule by launchd that auto-generates Instagram feature articles. A single run takes a long time (up to three claude -p calls, plus image generation, adding up to tens of minutes), so it has a lock mechani
AI 资讯
NiceGUI: crea una aplicación web en Python sin escribir JavaScript
Si sabes Python pero el frontend te frena, NiceGUI es una de las mejores noticias de los últimos años: te permite construir aplicaciones web completas —con botones, formularios, gráficos y navegación— usando solo Python. ¿Qué es NiceGUI? Es un framework construido sobre FastAPI (backend) y Quasar/Vue (frontend). Tú escribes Python; NiceGUI genera la interfaz en el navegador y mantiene sincronizado el estado por ti. No necesitas HTML, CSS ni JavaScript para empezar. Lo simple que es Una app con un botón que muestra un mensaje son literalmente cuatro líneas: from nicegui import ui ui . button ( ' Saludar ' , on_click = lambda : ui . notify ( ' ¡Hola! ' )) ui . run () La jerarquía de la interfaz se expresa con context managers , que reflejan cómo se anidan los elementos: with ui . card (): ui . label ( ' Iniciar sesión ' ). classes ( ' text-xl font-bold ' ) usuario = ui . input ( ' Usuario ' ) clave = ui . input ( ' Clave ' , password = True ) ui . button ( ' Entrar ' , on_click = lambda : entrar ( usuario . value , clave . value )) Añadir estado reactivo, formularios, tablas o rutas ( @ui.page('/panel') ) es igual de directo. ¿Para qué es ideal? MVPs y prototipos: validar una idea en días, no semanas. Dashboards internos y paneles de datos. Herramientas internas para tu equipo, sin montar un frontend aparte. Demos de modelos de IA o scripts que necesitan una interfaz. ¿Cuándo NO es la mejor opción? NiceGUI renderiza en el cliente, así que para sitios donde el SEO del contenido es crítico (un blog, una landing pública que debe posicionar) conviene complementarlo con buenas meta etiquetas y datos estructurados, o valorar renderizado en servidor. Para aplicaciones, dashboards y herramientas internas es una elección excelente y muy productiva. Rendimiento y despliegue Una app NiceGUI se despliega como cualquier app de FastAPI/Uvicorn, normalmente detrás de nginx con systemd. Sirve los estáticos desde nginx y usa ui.run(show=False) en producción para no abrir un navegador
AI 资讯
The Rust Awakens: Ownership Explained for JavaScript Devs
The Quest Begins (The "Why") Hey friend, picture this: you’re happily writing JavaScript, tossing objects around like confetti at a parade, and then you decide to give Rust a spin. You open the compiler, write a simple function that returns a slice of a vector, and boom— error[E0505]: cannot move out of … because it is borrowed . Your brain does a double‑take. “Wait, I didn’t even touch anything!” you mutter, staring at the screen like you just missed a plot twist in Inception . That moment was my dragon. I’d spent years trusting the garbage collector to clean up after me, and Rust’s ownership system felt like a strict sensei who wouldn’t let you leave the dojo until you bowed correctly. I was frustrated, curious, and honestly a little scared. But once I grasped the core ideas, the whole language started to click like a well‑oiled machine. So why does ownership matter? Because it gives you memory safety without a runtime garbage collector. No surprise pauses, no hidden allocations—just compile‑time guarantees that your program won’t dereference null or use‑after‑free. For a JS dev used to “it just works”, that’s a superpower worth earning. The Revelation (The Insight) The big surprise? Ownership isn’t just about who “owns” a value; it’s about how that value can be accessed, moved, or borrowed at any point in the program. Three rules govern everything: Each value has a single owner. When the owner goes out of scope, the value is dropped. You can either have one mutable reference or any number of immutable references to a value, but never both at the same time. Sounds simple, right? The gotcha is that Rust treats references as a separate kind of value with its own lifetime. If you try to store a reference beyond the lifetime of what it points to, the compiler says “nope”. This is where many JS devs stumble because in JavaScript a reference (or variable) just points to an object that lives as long as something else holds it—garbage collection decides when it’s gone. Le
开源项目
🔥 pipeshub-ai / pipeshub-ai - PipesHub is an open-source fully extensible AI context layer
GitHub热门项目 | PipesHub is an open-source fully extensible AI context layer that unifies your business data for explainable enterprise search and agentic workflow automation. | Stars: 3,405 | 50 stars today | 语言: Python
AI 资讯
GPT-4o API Costs Dropped 50% - How to Recalculate Your AI Budget
OpenAI has cut prices on its frontier models again. If you're running any production workload on the API, your cost assumptions from six months ago are probably stale. The Real Impact of a Pricing Halving A 50% price cut sounds like pure good news, but it changes the calculus on decisions you already made. Projects you shelved because the token costs didn't pencil out deserve a second look. Architectures you built around cheaper, less capable models to save money may now be false economies - the cost gap between "good enough" and "best available" just got smaller. The more interesting shift is for teams running retrieval-augmented generation (RAG) pipelines - systems that pull relevant documents from a database at query time and feed them into the model as context. RAG workflows tend to be token-heavy because every retrieved chunk counts against your input token bill. At the old pricing, teams were aggressively trimming context windows and limiting retrieved chunks to stay within budget. At half the cost, you can retrieve more, keep longer context, and let the model reason over richer information - without changing a line of retrieval logic. Real Example Here's a simplified cost check you can drop into any project that calls the OpenAI API: import openai # Approximate pricing per 1M tokens (check platform.openai.com for current rates) INPUT_COST_PER_1M = 2.50 # update to current figure OUTPUT_COST_PER_1M = 10.00 # update to current figure def estimate_cost ( input_tokens : int , output_tokens : int ) -> float : return ( input_tokens / 1_000_000 * INPUT_COST_PER_1M + output_tokens / 1_000_000 * OUTPUT_COST_PER_1M ) # Example: a RAG call with 3,000 input tokens and 500 output tokens print ( f " Estimated cost per call: $ { estimate_cost ( 3000 , 500 ) : . 5 f } " ) # Run this across your monthly volume to see the real delta Multiply that per-call number by your actual monthly call volume and compare it against what you budgeted. For many teams, the difference will jus
开发者
🚀 SoloEngine v0.4.0 Release
🚀 SoloEngine v0.4.0 Release — Context Compaction, Browser/Terminal Panels, Token Statistics...
AI 资讯
Designing AI Evals: Clarity Now and Visualization Next
AI evals and analysis Let's say you're testing out new AI tools. Perhaps you implement and...
AI 资讯
Your agent ignored a failed tool call. Here's how to catch that in CI.
You ship an AI agent. It calls tools, reads results, calls more tools, answers. Most of the time it works. Then a user reports something wrong, you open the trace, and you find it: the charge_card tool returned a 402, and the agent just... kept going and told the customer their order shipped. That's not a hallucination in the "made up a fact" sense. It's a structural defect in the run — an ignored tool error. And here's the thing about structural defects: you don't need another LLM to find them. They're decidable by looking at the trace. That's the whole premise of tracelint : a linter for agent runs. It reads the execution trace — what the agent actually did — and flags structural bugs deterministically, with the exact trace lines as evidence and a CI exit code. It runs after the run, on the trace, not on your code. No second model ever judges it. Why not just use an LLM judge? Because for this class of bug, a judge is the wrong tool. Published trace-error benchmarks show LLM judges have low localization accuracy — they'll tell you "something seems off" without reliably pointing at which step . They're also non-deterministic, cost money per trace, and can't gate CI (would you fail a build on a coin-flip?). Meanwhile, a whole category of agent bugs is structurally decidable : A tool call whose arguments violate the tool's JSON Schema. That's not an opinion — you run the schema validator. A tool that returned an error, followed by the agent proceeding as if it hadn't. The same tool called 5 times with identical arguments and identical results (a stuck loop). Arguments that don't appear anywhere in what the agent observed (a candidate hallucinated value). None of these needs a model. They need the trace and a validator. That's what tracelint does. The 60-second version pip install tracelint tracelint demo --html demo.html demo runs a keyless validation suite — one planted instance of every defect, plus clean controls — and writes an HTML report. No API key, no model d
AI 资讯
PromptShrink
How I Cut LLM Token Usage by Up to 60% in Production If you work with LLM APIs (OpenAI, Anthropic, Gemini), you know the pain: every call costs money, and a big chunk of that cost is pure waste — verbose prompts, code pasted with no filtering, repeated context the model doesn't even need to understand the task. That's why I built PromptShrink: a prompt pre-processor that trims the excess before it ever hits the API, without losing what actually matters for the model to understand. The real problem Every time you feed a code snippet or a long prompt to an LLM, you're paying per token, not per character. Comments, whitespace, formatting meant for humans — all of that is dead weight the model doesn't need to do its job. At scale (thousands of calls per month), that adds up to a real bill. What PromptShrink does Packages entire repositories, minifying code and stripping comments, ready to paste as context into any LLM Simulates real dollar savings, comparing your current spend against the optimized version Visualizes everything on a dashboard — tokens saved, % reduction, active rules Plugs straight into your code via a Python SDK Becomes a browser extension, adding a "Shrink" button directly on ChatGPT, Claude.ai, Google AI Studio, and Poe In practice bash Package an entire project into optimized context promptshrink repo --path ./src --save-to-file context.txt Simulate monthly savings promptshrink calc --calls 100000 --tokens 800 --model gpt-4o Running calc on a scenario of [insert your real number here, e.g. "100k calls/month with gpt-4o"], the estimated savings came out to [$X per month] — just by trimming what's unnecessary before it reaches the model. Try it out The project is open source, with a CLI, a FastAPI backend, and a Python SDK. If you're running LLMs in production and want to stop paying for tokens that add zero value, check it out: 🔗 github.com/HeloisaPeGarcia/PromptShrink Feedback and PRs are very welcome — this is my first published project like this,