<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sid Probstein</title>
    <description>The latest articles on DEV Community by Sid Probstein (@sidswirl).</description>
    <link>https://dev.to/sidswirl</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1132432%2F063b263f-a2ce-4db8-b6ff-b96cf0655d3c.jpeg</url>
      <title>DEV Community: Sid Probstein</title>
      <link>https://dev.to/sidswirl</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sidswirl"/>
    <language>en</language>
    <item>
      <title>Making RAG admit when it's guessing: source-grounded hallucination checks</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Wed, 01 Jul 2026 15:12:48 +0000</pubDate>
      <link>https://dev.to/sidswirl/making-rag-admit-when-its-guessing-source-grounded-hallucination-checks-g22</link>
      <guid>https://dev.to/sidswirl/making-rag-admit-when-its-guessing-source-grounded-hallucination-checks-g22</guid>
      <description>&lt;p&gt;The failure mode that scares me most in RAG isn't a wrong answer. It's a confident wrong answer with three citations that don't actually say what the answer claims.&lt;/p&gt;

&lt;p&gt;So in SWIRL 5 I stopped trusting the model to police itself and added a check that runs &lt;em&gt;after&lt;/em&gt; generation.&lt;/p&gt;

&lt;p&gt;The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate the answer with its citations, as usual.&lt;/li&gt;
&lt;li&gt;Split the answer into atomic claims — roughly one assertion per sentence.&lt;/li&gt;
&lt;li&gt;For each claim, pull the specific spans from the retrieved passages the model cited.&lt;/li&gt;
&lt;li&gt;Run an entailment check: does the cited text actually support this claim, contradict it, or neither?&lt;/li&gt;
&lt;li&gt;Any claim that isn't supported gets flagged in the UI, inline, before the user reads a word of it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The interesting part wasn't the entailment model; it was everything around it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claim segmentation is harder than it sounds.&lt;/strong&gt; Naive sentence splitting produces claims that are unverifiable on their own because the subject lives two sentences up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Citations lie by omission.&lt;/strong&gt; A model will cite a document that's &lt;em&gt;topically&lt;/em&gt; relevant but doesn't contain the specific number it just quoted. The whole point of the check is to catch exactly that gap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency budget.&lt;/strong&gt; An honesty layer nobody waits for is an honesty layer nobody ships. SWIRL 5 batches and optionally caches passage embeddings and more. &lt;/p&gt;

&lt;p&gt;The result isn't "SWIRL never hallucinates." Nothing can promise that. The result is: when it's on thin ice, it tells you, and it points at the exact sentence.&lt;/p&gt;

&lt;p&gt;That's the version of trustworthy I can actually build.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
    </item>
    <item>
      <title>SWIRL Community 4.5 Update</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Sun, 28 Jun 2026 16:30:28 +0000</pubDate>
      <link>https://dev.to/sidswirl/swirl-community-45-update-3bji</link>
      <guid>https://dev.to/sidswirl/swirl-community-45-update-3bji</guid>
      <description>&lt;p&gt;I wrote about SWIRL here last summer. Time for an update.&lt;/p&gt;

&lt;p&gt;SWIRL Community 4.5 is out. It is the open-source, Apache-2.0 build of SWIRL: federated search and RAG across your apps, running on your own machine, no vector database required. Three things in this release are worth your attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Point RAG at any LLM
&lt;/h2&gt;

&lt;p&gt;The big one. Community now lets you &lt;a href="https://docs.swirlaiconnect.com/rag-guide#configuring-rag" rel="noopener noreferrer"&gt;configure AI Providers&lt;/a&gt; and send your RAG queries to any model you want: OpenAI, Anthropic, Azure OpenAI, or a fully local model behind Ollama or vLLM. Pick the provider, set the model, done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ai_provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;anthropic&lt;/span&gt;      &lt;span class="c1"&gt;# openai | azure | ollama | vllm&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-model&lt;/span&gt;
  &lt;span class="na"&gt;api_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${LLM_KEY}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This matters for two reasons. RAG quality is now your choice, not ours, so you can run a stronger model when accuracy counts. And you can keep the whole pipeline local, with retrieval and generation both inside your network. This was an Enterprise feature; it is in Community as of 4.5.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Ask RAG a question directly
&lt;/h2&gt;

&lt;p&gt;RAG now accepts a natural-language question as its input. Before, you ran a search and generated over the results; now you can hand SWIRL a plain question and it does retrieval and generation in one step. Question in, cited answer out, which means a lot less glue code to wire SWIRL into a chat box or an app.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Updated Galaxy
&lt;/h2&gt;

&lt;p&gt;Community now includes the new Galaxy 5 UI, including a search history widget + dashboard, cleaned up admin interface, built-in activity analytics and more. &lt;/p&gt;

&lt;p&gt;That is the release. Free, self-hosted, and now model-agnostic.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>nlp</category>
      <category>opensource</category>
    </item>
    <item>
      <title>The knowledge-authority layer: what your agents can't get from the outside</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Wed, 17 Jun 2026 13:28:55 +0000</pubDate>
      <link>https://dev.to/sidswirl/the-knowledge-authority-layer-what-your-agents-cant-get-from-the-outside-f4i</link>
      <guid>https://dev.to/sidswirl/the-knowledge-authority-layer-what-your-agents-cant-get-from-the-outside-f4i</guid>
      <description>&lt;p&gt;Every enterprise AI conversation right now starts in the same place: "connect the model to our data." Then it stalls in the same place: &lt;em&gt;which&lt;/em&gt; data, copied &lt;em&gt;where&lt;/em&gt;, governed by &lt;em&gt;whom&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Let me make an argument that runs against the current default - and then show the architecture it implies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The default is a second copy of your data
&lt;/h2&gt;

&lt;p&gt;The standard RAG recipe is: crawl your sources, chunk them, embed them, and load the vectors into a database. Now your model can retrieve. It also means you have a &lt;em&gt;second copy&lt;/em&gt; of your content living in an index you have to secure, keep in sync, and explain to whoever owns compliance. You've recreated every permission boundary by hand, and you'll eventually get one wrong.&lt;/p&gt;

&lt;p&gt;For a lot of teams that copy is simply not allowed. Regulated content, client-confidential material, anything privileged - copying it into a vendor store is exposure you don't get paid to take on.&lt;/p&gt;

&lt;h2&gt;
  
  
  You probably don't need the vector database
&lt;/h2&gt;

&lt;p&gt;Here's the part people don't want to hear. Meta's XetHub team &lt;a href="https://web.archive.org/web/20251221204717/https://xethub.com/blog/you-dont-need-a-vector-database" rel="noopener noreferrer"&gt;benchmarked three retrieval strategies&lt;/a&gt;: keyword-only (BM25), vector-only, and hybrid (keyword to pull candidates, then re-rank). Keyword-only came last. Vector-only did better. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hybrid won&lt;/strong&gt; - and their conclusion was blunt: "No vector database necessary."&lt;/p&gt;

&lt;p&gt;That matches what we see in production. Vector similarity is a great &lt;em&gt;high-precision filter&lt;/em&gt;, not a great &lt;em&gt;first pass&lt;/em&gt;. Lead with exact matches and quoted terms, then let embeddings and a cross-encoder re-rank what's left.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "make your LLM better" actually means
&lt;/h2&gt;

&lt;p&gt;It's not a slogan; it's a pipeline. In SWIRL, relevance is three passes, and both models run locally:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Federate and match.&lt;/strong&gt; Query every connected source in parallel - keyword + BM25 - and honor quoted phrases and exact terms first.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedding re-rank.&lt;/strong&gt; Re-rank candidates with &lt;code&gt;E5-large-v2&lt;/code&gt;, using title-aware chunking and hybrid keyword+vector fusion (RRF). No vector database to build or secure.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-encoder re-rank.&lt;/strong&gt; An &lt;code&gt;MS-MARCO&lt;/code&gt; cross-encoder reads the query and document &lt;em&gt;together&lt;/em&gt; and scores real relevance, not vector distance.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Feed &lt;em&gt;that&lt;/em&gt; to your LLM - whatever model you've chosen, including an on-prem one - and the answer gets better, because the context got better. Same model, sharper input.&lt;/p&gt;

&lt;h2&gt;
  
  
  The layer no model supplies from the outside
&lt;/h2&gt;

&lt;p&gt;The stack is settling: foundation models orchestrate, MCP is the retrieval interface, the chat UI is a commodity. The piece none of them provide from outside your walls is &lt;strong&gt;knowledge authority&lt;/strong&gt; - which document is official, which clause your org actually uses, which answer carries approval.&lt;/p&gt;

&lt;p&gt;So we made it a first-class layer. SWIRL 5 exposes an MCP server. Any agent - Claude, Copilot, ChatGPT, your own - calls SWIRL and gets ranked, permissioned, &lt;em&gt;organization-approved&lt;/em&gt; answers. A team pins the canonical result for a query once; every agent gets it after that. And no copy of your data leaves your tenant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this shape
&lt;/h2&gt;

&lt;p&gt;Three properties fall out of it, and they're the whole reason to build it this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Private by architecture.&lt;/strong&gt; Data stays in place; permissions are enforced live; there's no second index to govern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The answer, not a guess.&lt;/strong&gt; Cross-encoder ranking plus canonical answers means people and agents get the result the org trusts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The safe on-ramp to AI.&lt;/strong&gt; Headless and MCP-native, deployed in your tenant - the lowest-risk way to give agents enterprise reach.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're wiring agents into enterprise data and the "just copy everything into a vector store" step is making your security team twitch, there's another shape available. SWIRL 5 goes GA July 15; the preview is open if you want to point it at your own stack. Either way - I'd genuinely like to hear how you're handling the authority problem, because I don't think the industry has it figured out yet.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sid Probstein is the creator of SWIRL and CEO of SWIRL AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rag</category>
      <category>llm</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Moving Docker images between repos with crane (after imagetools wasted my afternoon)</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Thu, 11 Jun 2026 17:55:15 +0000</pubDate>
      <link>https://dev.to/sidswirl/moving-docker-images-between-repos-with-crane-after-imagetools-wasted-my-afternoon-bjg</link>
      <guid>https://dev.to/sidswirl/moving-docker-images-between-repos-with-crane-after-imagetools-wasted-my-afternoon-bjg</guid>
      <description>&lt;p&gt;I had a freshly-built, multi-arch dev image (&lt;code&gt;linux/amd64&lt;/code&gt; + &lt;code&gt;linux/arm64&lt;/code&gt;) and one job: promote it into a private partner repo on Docker Hub, plus stamp a dated tag so &lt;code&gt;:latest&lt;/code&gt; is always traceable back to a real build. Cross-repo. Should be five minutes.&lt;/p&gt;

&lt;p&gt;It was not five minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;The obvious tool is &lt;code&gt;docker buildx imagetools create&lt;/code&gt;... it's built for copying manifests between tags. So I reached for it. And it sat there. Then it 400'd. Then I retried, and it hung. Cross-repo blob copies on Docker Hub are reproducibly flaky with imagetools, and I burned the better part of an hour confirming that before I went looking for something else.&lt;/p&gt;

&lt;p&gt;The fallback most people reach for next is worse: pull the image down, retag it, push it to the new repo. That round-trips the &lt;em&gt;entire&lt;/em&gt; image — every layer, every arch - through your laptop's daemon and disk, only to push the same bits back up. And if you're not careful with how you tag, you flatten a multi-arch index down to whatever single arch your machine happens to be. No thanks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnetefv8hsa8yrjqd3ix.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxnetefv8hsa8yrjqd3ix.png" alt=" " width="799" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why crane
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/google/go-containerregistry/tree/main/cmd/crane" rel="noopener noreferrer"&gt;&lt;code&gt;crane&lt;/code&gt;&lt;/a&gt; (from Google's go-containerregistry) does the copy &lt;strong&gt;registry-to-registry&lt;/strong&gt;. It never pulls the image to your machine.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It preserves the multi-arch manifest.&lt;/strong&gt; &lt;code&gt;crane cp&lt;/code&gt; copies the whole image &lt;em&gt;index&lt;/em&gt; by digest. Both arches come along. Nothing gets flattened.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It actually works cross-repo on Docker Hub.&lt;/strong&gt; Where imagetools 400'd and hung, crane did the server-to-server copy in seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No daemon, no disk.&lt;/strong&gt; It talks to the registries directly. Your laptop just orchestrates.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    subgraph slow["pull → tag → push"]
        A[source repo&amp;lt;br/&amp;gt;you/app:dev] --&amp;gt; L[your daemon + disk&amp;lt;br/&amp;gt;whole image] --&amp;gt; B[partner repo&amp;lt;br/&amp;gt;you/partner-app:latest]
    end
    subgraph crane["crane cp"]
        C[source repo&amp;lt;br/&amp;gt;you/app:dev] --&amp;gt;|manifest + blobs&amp;lt;br/&amp;gt;by digest| D[partner repo&amp;lt;br/&amp;gt;you/partner-app:latest]
    end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run it as a container, no install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; gcr.io/go-containerregistry/crane:debug &amp;lt;crane-args&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole installation story. Nothing on the host.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth issues (MacOS)
&lt;/h2&gt;

&lt;p&gt;crane in the container reads &lt;code&gt;~/.docker/config.json&lt;/code&gt;. On &lt;strong&gt;macOS with Docker Desktop&lt;/strong&gt;, your login isn't &lt;em&gt;in&lt;/em&gt; that file — it's &lt;code&gt;credsStore: osxkeychain&lt;/code&gt;, sitting in the macOS keychain. So if you naively mount &lt;code&gt;~/.docker/config.json&lt;/code&gt; into the container, crane sees no usable credential and hands you &lt;code&gt;UNAUTHORIZED&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The fix: pull the credential out of the keychain on the host, write it into a &lt;em&gt;temporary inline&lt;/em&gt; config, mount that, and delete it the moment you're done. Never print it, never commit it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TMP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'rm -rf "$TMP"'&lt;/span&gt; EXIT
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TMP&lt;/span&gt;&lt;span class="s2"&gt;/mkcfg.py"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;PY&lt;/span&gt;&lt;span class="sh"&gt;'
import json, sys, base64
d = json.load(sys.stdin)
auth = base64.b64encode((d["Username"] + ":" + d["Secret"]).encode()).decode()
json.dump({"auths": {"https://index.docker.io/v1/": {"auth": auth}}}, open(sys.argv[1], "w"))
&lt;/span&gt;&lt;span class="no"&gt;PY
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"https://index.docker.io/v1/"&lt;/span&gt; | docker-credential-osxkeychain get | python3 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TMP&lt;/span&gt;&lt;span class="s2"&gt;/mkcfg.py"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TMP&lt;/span&gt;&lt;span class="s2"&gt;/config.json"&lt;/span&gt;

CRANE&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TMP&lt;/span&gt;&lt;span class="s2"&gt;/config.json"&lt;/span&gt;:/root/.docker/config.json:ro &lt;span class="se"&gt;\&lt;/span&gt;
            gcr.io/go-containerregistry/crane:debug &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;trap ... EXIT&lt;/code&gt; cleans up the temp config when your shell exits, so the credential doesn't linger.&lt;/p&gt;

&lt;p&gt;On &lt;strong&gt;Linux&lt;/strong&gt; (or anywhere &lt;code&gt;docker login&lt;/code&gt; writes inline creds), skip all of that and mount &lt;code&gt;~/.docker/config.json&lt;/code&gt; directly. And obvious-but-worth-saying: you need a &lt;strong&gt;read/write&lt;/strong&gt; login on the destination. A read-only token can't push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Promote the image
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# copy SRC -&amp;gt; DST, full multi-arch manifest, server to server:&lt;/span&gt;
CRANE &lt;span class="nb"&gt;cp &lt;/span&gt;you/app:dev &lt;span class="se"&gt;\&lt;/span&gt;
         you/partner-app:latest

&lt;span class="c"&gt;# also stamp a dated, immutable tag so :latest is always traceable to a build:&lt;/span&gt;
CRANE &lt;span class="nb"&gt;cp &lt;/span&gt;you/app:dev &lt;span class="se"&gt;\&lt;/span&gt;
         you/partner-app:dev-2026-06-11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two copies, both registry-side, both done before you can refill your coffee.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verification
&lt;/h2&gt;

&lt;p&gt;Don't trust that the copy worked. Prove it. The destination digest must equal the source digest — same bits, same manifest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CRANE digest you/app:dev                 &lt;span class="c"&gt;# source&lt;/span&gt;
CRANE digest you/partner-app:latest      &lt;span class="c"&gt;# must equal source&lt;/span&gt;
CRANE digest you/partner-app:dev-2026-06-11   &lt;span class="c"&gt;# must equal source&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If those three lines match, the right image landed under both tags. If they don't, you copied the wrong thing — better to find out here than in a partner's deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CRANE &lt;span class="nb"&gt;ls &lt;/span&gt;you/partner-app                          &lt;span class="c"&gt;# list tags&lt;/span&gt;
CRANE manifest you/app:dev                         &lt;span class="c"&gt;# full manifest JSON, see the arches&lt;/span&gt;
CRANE tag you/app@sha256:&amp;lt;digest&amp;gt; newtag           &lt;span class="c"&gt;# add a tag to an existing digest&lt;/span&gt;
CRANE copy &amp;lt;SRC&amp;gt; &amp;lt;DST&amp;gt;                             &lt;span class="c"&gt;# alias of cp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One caveat worth knowing: tag &lt;em&gt;deletion&lt;/em&gt; isn't a crane operation. Use the Docker Hub UI or the API for that (and it needs an admin-scoped token — a read/write PAT won't delete).&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Reach for crane any time you're moving an image &lt;em&gt;between registries or repos&lt;/em&gt; and you care about the manifest arriving intact — promotions, mirrors, cross-org handoffs. It skips the daemon, skips the disk, and it doesn't fall over on Docker Hub cross-repo copies the way imagetools does. And whatever you do, build the &lt;code&gt;crane digest&lt;/code&gt; source-equals-dest check into the workflow. It costs two seconds.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>resources</category>
    </item>
    <item>
      <title>Running PyTorch fork-safe in Celery on macOS</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Mon, 01 Jun 2026 13:23:46 +0000</pubDate>
      <link>https://dev.to/sidswirl/running-pytorch-fork-safe-in-celery-on-macos-4h6a</link>
      <guid>https://dev.to/sidswirl/running-pytorch-fork-safe-in-celery-on-macos-4h6a</guid>
      <description>&lt;p&gt;If you've ever seen this in your Celery logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Process 'ForkPoolWorker-7' pid:32839 exited with 'signal 11 (SIGSEGV)'
billiard.exceptions.WorkerLostError:
    Worker exited prematurely: signal 11 (SIGSEGV) Job: 0.

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...and the macOS crash report buries the real message in a JSON blob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"asi"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"CoreFoundation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"*** multi-threaded process forked ***"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"libsystem_c.dylib"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"crashed on child side of fork pre-exec"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...you've hit one of the classic fork-after-init traps. Here's what's going on and how to actually fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line fix (if you're in a hurry)
&lt;/h2&gt;

&lt;p&gt;Set these env vars before the Celery worker's MainProcess imports anything heavy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# &amp;lt;your-project&amp;gt;/celery.py ... the very first thing the worker imports
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENBLAS_NUM_THREADS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OMP_NUM_THREADS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;MKL_NUM_THREADS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;NUMEXPR_NUM_THREADS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;VECLIB_MAXIMUM_THREADS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setdefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OBJC_DISABLE_INITIALIZE_FORK_SAFETY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;YES&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first set forces every BLAS library to single-threaded mode. VECLIB_MAXIMUM_THREADS is the one most people forget; it covers Apple's Accelerate framework, which is what PyTorch uses by default on Apple Silicon. The last one tells the Objective-C runtime to skip its fork-safety abort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this happens
&lt;/h2&gt;

&lt;p&gt;PyTorch's nn.Linear on macOS arm64 calls into Apple Accelerate, which does its parallel matmuls via libdispatch (Grand Central Dispatch).&lt;/p&gt;

&lt;p&gt;The first BLAS call lazily spins up a pool of libdispatch worker queues in the calling process.&lt;/p&gt;

&lt;p&gt;If that "calling process" is your Celery worker's MainProcess (say, because something during boot does a tiny matmul: spaCy preload, an embedding warmup, anything that imports numpy and runs a real op), those queues now live in the parent. &lt;/p&gt;

&lt;p&gt;When the prefork pool then fork()s a child, the child inherits broken queue handles. The next BLAS call from inside the child dereferences a stale pointer and you get the SIGSEGV.&lt;/p&gt;

&lt;p&gt;The stack trace in the crash report makes it unambiguous:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0: _dispatch_apply_with_attr_f      (libdispatch)
1: dispatch_apply_with_attr         (libdispatch)
3: cblas_sgemm                      (Accelerate)
5: at::native::cpublas::gemm        (libtorch_cpu)
6: at::native::addmm_impl_cpu_      (libtorch_cpu)
7: at::native::linear               (libtorch_cpu)
8: torch::autograd::THPVariable_linear
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What doesn't work
&lt;/h2&gt;

&lt;p&gt;"Just lazy-load the model in the child." Even if you defer from_pretrained until you're inside a forked child, that first call still hits Accelerate BLAS, and the dispatch queues your child inherited from the parent are already broken.&lt;/p&gt;

&lt;p&gt;"Just bypass sentence_transformers.CrossEncoder.predict() and use bare-torch." Same story. Whether you go through CrossEncoder or call AutoModelForSequenceClassification directly, the SIGSEGV is one frame down inside linear().&lt;/p&gt;

&lt;p&gt;"Just don't import torch at the top of the module." Necessary but not sufficient. In our case, removing import torch from ai_provider.py was real progress, but then we discovered litellm transitively pulls torch the first time you call it. Every "warmup" preload that touched litellm still poisoned the parent. You have to audit every code path that runs before the first fork.&lt;/p&gt;

&lt;h2&gt;
  
  
  The defensive pattern that does work
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Defer heavy imports. Don't import torch at module top in anything that's part of the Celery autodiscovery chain. Push it into the function that needs it:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Bad ... taints anyone who imports this module
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rerank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# Good ... torch only loads in workers that actually rerank
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;rerank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;no_grad&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Gate "warmup" preloads off the Celery worker. Preloading models at startup makes sense for an ASGI server like Daphne. It is actively harmful in a forking Celery worker, because the warmup runs in MainProcess:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyAppConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AppConfig&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;is_celery_worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;celery&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;worker&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;is_celery_worker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_preload_cross_encoder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What about Linux / Docker?
&lt;/h2&gt;

&lt;p&gt;Yes, this affects Linux too, just less dramatically. &lt;/p&gt;

&lt;p&gt;OpenBLAS and MKL both spin up thread pools on first use that don't survive fork; the typical Linux failure mode is a hang or a deadlock rather than a SIGSEGV. &lt;/p&gt;

&lt;p&gt;The good news: the same *_NUM_THREADS=1 env vars are the fix. &lt;/p&gt;

&lt;p&gt;VECLIB_MAXIMUM_THREADS and OBJC_DISABLE_INITIALIZE_FORK_SAFETY are no-ops on Linux, so the snippet above is portable. The deferred-import and gate-off-warmup patterns apply unchanged.&lt;/p&gt;

</description>
      <category>python</category>
      <category>celery</category>
    </item>
    <item>
      <title>The AI agent stack that’s quietly taking over enterprise workflows</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Sat, 03 May 2025 02:58:27 +0000</pubDate>
      <link>https://dev.to/sidswirl/the-ai-agent-stack-thats-quietly-taking-over-enterprise-workflows-2h8o</link>
      <guid>https://dev.to/sidswirl/the-ai-agent-stack-thats-quietly-taking-over-enterprise-workflows-2h8o</guid>
      <description>&lt;p&gt;Accenture, IBM, and AWS are all placing bets on &lt;a href="https://www.crewai.com/" rel="noopener noreferrer"&gt;Crew AI&lt;/a&gt;. Why? Because it makes building and deploying real AI agents possible.&lt;/p&gt;

&lt;p&gt;With Crew AI, teams are spinning up agents that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launch predictive marketing campaigns&lt;/li&gt;
&lt;li&gt;Automate financial back-office ops&lt;/li&gt;
&lt;li&gt;Optimize inventory and logistics&lt;/li&gt;
&lt;li&gt;And tackle 100+ other enterprise use cases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here’s the catch: agents are only as good as the data they can reach. That’s where &lt;a href="https://swirlaiconnect.com/" rel="noopener noreferrer"&gt;SWIRL&lt;/a&gt; comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo016mxcb9nx03auythy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbo016mxcb9nx03auythy.png" alt=" " width="800" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By pairing Crew AI with SWIRL, you get more than just agents—you get enterprise-ready, data-rich workflows that scale. No custom plumbing. No brittle integrations.&lt;/p&gt;

&lt;p&gt;With Crew AI + SWIRL, your agents can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connect to &lt;a href="https://swirlaiconnect.com/connectors" rel="noopener noreferrer"&gt;100+ enterprise data sources&lt;/a&gt; out-of-the-box&lt;/li&gt;
&lt;li&gt;Fetch the most relevant structured/unstructured data across silos&lt;/li&gt;
&lt;li&gt;Respect row-level permissions with real enterprise auth&lt;/li&gt;
&lt;li&gt;Summarize and answer with your LLM of choice&lt;/li&gt;
&lt;li&gt;Plug in easily via zero-code connectors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Want to see this in action? &lt;/p&gt;

&lt;p&gt;Message me for a demo or check the open source edition here: &lt;a href="https://github.com/swirlai/swirl-search" rel="noopener noreferrer"&gt;https://github.com/swirlai/swirl-search&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>showdev</category>
    </item>
    <item>
      <title>It was already indexed!</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Sun, 13 Aug 2023 16:48:52 +0000</pubDate>
      <link>https://dev.to/sidswirl/it-was-already-indexed-2b4</link>
      <guid>https://dev.to/sidswirl/it-was-already-indexed-2b4</guid>
      <description>&lt;p&gt;I recently had the pleasure of chatting with &lt;a class="mentioned-user" href="https://dev.to/dmitrykan"&gt;@dmitrykan&lt;/a&gt; on his  &lt;a href="https://dmitry-kan.medium.com/vector-podcast-e27d83ecd0be" rel="noopener noreferrer"&gt;Vector Podcast&lt;/a&gt;. Check it out: &lt;a href="https://dmitry-kan.medium.com/vector-podcast-with-sid-probstein-search-in-siloed-data-with-swirl-f2b9595a2715" rel="noopener noreferrer"&gt;https://dmitry-kan.medium.com/vector-podcast-with-sid-probstein-search-in-siloed-data-with-swirl-f2b9595a2715&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We talked about quite a few things, including: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The challenges of enterprise search in the post-cloud era&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How cross-silo search is particularlyt tricky because of entitlements (aka permissions) across silos&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Zero-code configuration of connectors in Swirl, where JSON path and developer API doc get the job done &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How large language models contextually re-rank disparate search results&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There was an interesting twist at the end of the call. Dmitry uses a service called &lt;a href="https://clearword.com/" rel="noopener noreferrer"&gt;Clearword&lt;/a&gt; to transcribe recordings. Dmitry asked: “how quickly can you index the transcript and search it with Swirl?” &lt;/p&gt;

&lt;p&gt;Here is my answer: &lt;a href="https://www.youtube.com/watch?v=WMHnb_6Wf50" rel="noopener noreferrer"&gt;It was already indexed!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/WMHnb_6Wf50"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Since there is no audio and it goes by quickly, let me explain ... Clearword emailed the transcript to both of us shortly after recording ended. It was indexed by Microsoft Outlook within seconds of arriving in my inbox. &lt;/p&gt;

&lt;p&gt;To verify this, I copied some text from the middle of the transcript and pasted it into Swirl, which returned the link to the email message with the transcript and the phrase I searched for. &lt;/p&gt;

&lt;p&gt;That simple truth - that the average enterprise is awash in search forms - is the entire reason metasearch is such a game-changing approach. Instead of making yet another repository, Swirl sends queries to existing search APIs and re-ranks the results from everything. It saves users a huge amount of time without a major IT project. &lt;/p&gt;

&lt;p&gt;Want to see for yourself? git it going with 2 commands via Docker here: &lt;a href="https://github.com/swirlai/swirl-search" rel="noopener noreferrer"&gt;https://github.com/swirlai/swirl-search&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Swirl 2.5 released</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Wed, 09 Aug 2023 14:37:03 +0000</pubDate>
      <link>https://dev.to/sidswirl/swirl-25-released-40g4</link>
      <guid>https://dev.to/sidswirl/swirl-25-released-40g4</guid>
      <description>&lt;p&gt;&lt;strong&gt;I am delighted to announce availability of Swirl 2.5!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi78nz5551etm2kuo0j5v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi78nz5551etm2kuo0j5v.png" alt=" " width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This version focused on performance. Configured with 12 SearchProviders, Swirl 2.5 supports ~15 queries/second on a Standard F16s v2 server (16 vcpus, 32 GiB memory) with a median response time of ~3 seconds. &lt;/p&gt;

&lt;p&gt;Version 2.5 also includes SearchProviders for HubSpot contact, company, and deal records, plus improvements to the Galaxy search UI (shown above).&lt;/p&gt;

&lt;p&gt;Check out the Release notes for full details: &lt;a href="https://github.com/swirlai/swirl-search/releases/tag/v2.5.0" rel="noopener noreferrer"&gt;https://github.com/swirlai/swirl-search/releases/tag/v2.5.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Swirl?&lt;/strong&gt; A new open source metasearch engine; it queries anything with an API then uses spaCy to re-rank the unified results without copying any data! Includes zero-code configs for Apache Solr, ChatGPT, Elastic Search, OpenSearch, PostgreSQL, Google BigQuery, RequestsGet, Google PSE, NLResearch.com, Miro, Microsoft 365, HubSpot, Atlassian, YouTrack, GitHub &amp;amp; more! &lt;/p&gt;

</description>
      <category>search</category>
      <category>python</category>
      <category>django</category>
    </item>
    <item>
      <title>I wrote a metasearch engine called Swirl</title>
      <dc:creator>Sid Probstein</dc:creator>
      <pubDate>Thu, 03 Aug 2023 18:48:40 +0000</pubDate>
      <link>https://dev.to/sidswirl/i-wrote-a-metasearch-engine-called-swirl-23i9</link>
      <guid>https://dev.to/sidswirl/i-wrote-a-metasearch-engine-called-swirl-23i9</guid>
      <description>&lt;p&gt;Hi all! &lt;a href="https://github.com/sidprobstein/swirl-search" rel="noopener noreferrer"&gt;Swirl&lt;/a&gt; sends queries to existing search engines, unifies the results and re-ranks them all using large language models. It solves cross-silo information access and search problems in a fraction of the time and effort required to copy, ingest and index data. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo4d13eov2335nmcxjy3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgo4d13eov2335nmcxjy3.png" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a brief video intro: &lt;a href="https://youtu.be/sfsBYyu6qDQ" rel="noopener noreferrer"&gt;https://youtu.be/sfsBYyu6qDQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Swirl was written in python atop the django/celery/redis stack with a choice of Sqlite3 or PostgreSQL back-ends. The source code is available under the Apache 2.0 license. The distribution includes zero-code configs for Apache Solr, ChatGPT, Elastic Search, OpenSearch, PostgreSQL, Google BigQuery, RequestsGet, Google PSE, NLResearch.com, Miro, Microsoft 365, Atlassian, YouTrack, GitHub, HubSpot &amp;amp; more. Plug-in your access tokens or use Microsoft 365 to login and users can stop searching and start Swirling!&lt;/p&gt;

&lt;p&gt;Links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sidprobstein/swirl-search" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.swirl.today" rel="noopener noreferrer"&gt;Swirl website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://join.slack.com/t/swirlmetasearch/shared_invite/zt-1qk7q02eo-kpqFAbiZJGOdqgYVvR1sfw" rel="noopener noreferrer"&gt;Slack channel for support&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We are seeking contributions and feedback from developers working on all kinds of search solutions... thanks!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl07sutgaxaulu25xvqm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgl07sutgaxaulu25xvqm.jpg" alt="Image description" width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>search</category>
      <category>cloudcomputing</category>
      <category>llm</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
