<?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: Frank Boucher ☁</title>
    <description>The latest articles on DEV Community by Frank Boucher ☁ (@fboucheros).</description>
    <link>https://dev.to/fboucheros</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F140121%2Fdc4cba6f-e82f-44cc-a754-d7c803bad6d3.jpg</url>
      <title>DEV Community: Frank Boucher ☁</title>
      <link>https://dev.to/fboucheros</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fboucheros"/>
    <language>en</language>
    <item>
      <title>Bringing Reka Edge to Your Workflow via OpenRouter</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Tue, 14 Apr 2026 13:22:11 +0000</pubDate>
      <link>https://dev.to/reka/bringing-reka-edge-to-your-workflow-via-openrouter-2bgh</link>
      <guid>https://dev.to/reka/bringing-reka-edge-to-your-workflow-via-openrouter-2bgh</guid>
      <description>&lt;p&gt;Reka Edge, Reka's latest multimodal model, is now available on OpenRouter. In a previous post we ran the model locally using vLLM. This time, we connect the same app to OpenRouter so we can switch between models by changing a connection string and a model ID.&lt;/p&gt;

&lt;p&gt;More of a visual learner? Grab some popcorn and watch the video. Prefer to read? Skip right past it, the full walkthrough continues below.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/0SZhcPe7ljQ"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The full source code is available at &lt;a href="https://github.com/fboucher/media-library" rel="noopener noreferrer"&gt;github.com/fboucher/media-library&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why Use OpenRouter?
&lt;/h2&gt;

&lt;p&gt;OpenRouter is the largest AI gateway for developers. It gives you a single, unified API to access all major models. The OpenAI SDK works out of the box, so there is nothing new to learn.&lt;/p&gt;

&lt;p&gt;Beyond convenience, OpenRouter provides higher availability through distributed infrastructure with automatic fallback when a provider goes down. It runs at the edge for minimal latency, offers better prices with no subscriptions, and lets you set custom data policies so prompts only go to models and providers you trust.&lt;/p&gt;

&lt;p&gt;Reka Edge and Reka Flash are both available on OpenRouter. Search for "Reka" or visit the &lt;a href="https://openrouter.ai/provider/reka" rel="noopener noreferrer"&gt;Reka provider page&lt;/a&gt; to get started.&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%2F2lksl96j8d6ay4avr42c.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%2F2lksl96j8d6ay4avr42c.png" alt="The OpenRouter interface showing available Reka models" width="800" height="900"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Need from OpenRouter
&lt;/h2&gt;

&lt;p&gt;The Quick Start guide on OpenRouter provides examples for Python, TypeScript, and other SDKs. To connect any app, you need three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;An API Key&lt;/strong&gt; to authenticate your requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The endpoint URL&lt;/strong&gt;: &lt;code&gt;https://openrouter.ai/api/v1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model ID&lt;/strong&gt; for the model you want to use. In our case, &lt;code&gt;rekaai/reka-edge&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Seeing It in Action
&lt;/h2&gt;

&lt;p&gt;In the previous &lt;a href="https://dev.to/reka/how-to-serve-a-vision-ai-model-locally-with-vllm-and-reka-edge-5h2i"&gt;post&lt;/a&gt;, we used a media-library app to run Reka Edge locally with vLLM. Let's use the same app and add a new OpenRouter connection to it. All we need to do is pass the three pieces of information collected above: the URL, the API key, and the model ID.&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%2F32ei5d15o1zm00p6ukwk.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%2F32ei5d15o1zm00p6ukwk.jpg" alt="Configuring a new connection named OpenRouter with the API key and model ID rekaai/reka-edge" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once saved, switch to the OpenRouter connection from the dropdown, upload an image, and click "Fill with AI." Reka Edge processes the image through OpenRouter and returns a detailed description. No changes to the application code were needed.&lt;/p&gt;

&lt;p&gt;The beauty of OpenRouter is exactly this: you just change the connection string and the model. Both Reka Edge and Reka Flash are available, and you could have all your models centralized in one place.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>How to Serve a Vision AI Model Locally with vLLM and Reka Edge</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Wed, 08 Apr 2026 13:16:10 +0000</pubDate>
      <link>https://dev.to/reka/how-to-serve-a-vision-ai-model-locally-with-vllm-and-reka-edge-5h2i</link>
      <guid>https://dev.to/reka/how-to-serve-a-vision-ai-model-locally-with-vllm-and-reka-edge-5h2i</guid>
      <description>&lt;p&gt;Running an AI model as a one-shot script is useful, but it forces you to restart the model every time you need a result. Setting it up as a service lets any application send requests to it continuously, without reloading the model. This guide shows how to serve &lt;strong&gt;Reka Edge&lt;/strong&gt; using &lt;strong&gt;vLLM&lt;/strong&gt; and an open-source plugin, then connect a web app to it for image description and object detection.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The vLLM plugin is available at &lt;a href="https://github.com/reka-ai/vllm-reka" rel="noopener noreferrer"&gt;github.com/reka-ai/vllm-reka&lt;/a&gt;. The demo Media Library app is at &lt;a href="https://github.com/fboucher/media-library" rel="noopener noreferrer"&gt;github.com/fboucher/media-library&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need a machine with a GPU and either Linux, macOS, or Windows (with WSL). I use &lt;a href="https://astral.sh/uv" rel="noopener noreferrer"&gt;UV&lt;/a&gt;, a fast Python package and project manager, or &lt;code&gt;pip&lt;/code&gt; + &lt;code&gt;venv&lt;/code&gt; if you prefer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone the vLLM Reka Plugin
&lt;/h2&gt;

&lt;p&gt;Reka models require a dedicated plugin to run under vLLM, not all models need this extra step, but Reka's architecture requires it. Clone the plugin repository and enter the directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/reka-ai/vllm-reka
&lt;span class="nb"&gt;cd &lt;/span&gt;vllm-reka
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository contains the plugin code and a &lt;code&gt;serve.sh&lt;/code&gt; script you will use to start the service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download the Reka Edge Model
&lt;/h2&gt;

&lt;p&gt;Before starting the service, you need the model weights locally. Install the Hugging Face Hub CLI and use it to pull the &lt;code&gt;reka-edge-2603&lt;/code&gt; model into your project directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv &lt;span class="nb"&gt;sync
&lt;/span&gt;uv pip &lt;span class="nb"&gt;install &lt;/span&gt;huggingface_hub
uvx hf download RekaAI/reka-edge-2603 &lt;span class="nt"&gt;--local-dir&lt;/span&gt; ./models/reka-edge-2603
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a large model, so make sure you have enough disk space and a stable connection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start the Service
&lt;/h2&gt;

&lt;p&gt;Once the model is downloaded, start the vLLM service using the &lt;code&gt;serve.sh&lt;/code&gt; script included in the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run bash serve.sh ./models/reka-edge-2603
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script accepts environment variables to configure which model to load and how much GPU memory to allocate. If your GPU cannot fit the model at default settings, open &lt;code&gt;serve.sh&lt;/code&gt; and adjust the variables at the top. The repository README lists the available options. The service takes a few seconds to load the model weights, then starts listening for HTTP requests.&lt;/p&gt;

&lt;p&gt;As an example with an &lt;em&gt;NVIDIA GeForce RTX 5070&lt;/em&gt;, here are the settings I used to run the model:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GPU_MEM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.80
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MAX_LEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4096
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MAX_BATCH_TOKENS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4096
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MAX_IMAGES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MAX_VIDEOS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;VIDEO_NUM_FRAMES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;4
uv run bash serve.sh ./models/reka-edge-2603
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Connect the Media Library App
&lt;/h2&gt;

&lt;p&gt;With the backend running, time to start the Media Library app. Clone the repository, jump into the directory, and run it with Docker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/fboucher/media-library
&lt;span class="nb"&gt;cd &lt;/span&gt;media-library
docker compose up &lt;span class="nt"&gt;--build&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt; in your browser, then add a new connection with these settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Name&lt;/strong&gt;: local (or any label you want)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IP address&lt;/strong&gt;: your machine's local network IP (e.g. &lt;code&gt;192.168.x.x&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API key&lt;/strong&gt;: leave blank or enter anything — no key is required for a local connection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model&lt;/strong&gt;: &lt;code&gt;reka-edge-2603&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Test&lt;/strong&gt; to confirm the connection, then save it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It: Image Description and Object Detection
&lt;/h2&gt;

&lt;p&gt;Select an image in the app and choose your local connection, then click &lt;strong&gt;Fill with AI&lt;/strong&gt;. The app sends the image to your vLLM service, and the model returns a natural language description. You can watch the request hit your backend in the terminal where the service is running.&lt;/p&gt;

&lt;p&gt;Reka Edge also supports object detection. Type a prompt asking the model to locate a specific feature (ex: "face") and the model returns bounding-box coordinates. The app renders these as red boxes overlaid on the image. This works for any region you can describe in a prompt.&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%2Fhkkjo2ghk0y67nvn5vxt.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%2Fhkkjo2ghk0y67nvn5vxt.png" alt="face detected" width="479" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Switch to the Reka Cloud API
&lt;/h2&gt;

&lt;p&gt;If your local GPU is too slow for production use, you can point the app at the Reka APIs instead. Add a new connection in the app and set the base URL to the Reka API endpoint. Get your API key from &lt;a href="https://platform.reka.ai" rel="noopener noreferrer"&gt;platform.reka.ai&lt;/a&gt;. &lt;a href="https://openrouter.ai" rel="noopener noreferrer"&gt;OpenRouter&lt;/a&gt; is another option if you prefer a unified API across providers.&lt;/p&gt;

&lt;p&gt;The model name stays the same (&lt;code&gt;reka-edge-2603&lt;/code&gt;), so switching between local and cloud is just a matter of selecting a different connection in the app. The cloud API is noticeably faster because Reka's servers are more powerful than a local GPU (at least mine :) ). During development, use the local service to avoid burning credits; switch to the API for speed when you need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Build
&lt;/h2&gt;

&lt;p&gt;The service you just set up accepts any image, or video via HTTP — point a script at a folder and you have a batch pipeline for descriptions, tags, or bounding boxes. Swap the prompt and you change what it extracts. The workflow is the same whether you are running locally or through the API.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/eyS_GWw-p54"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reka Edge model: &lt;a href="https://huggingface.co/RekaAI/reka-edge-2603" rel="noopener noreferrer"&gt;huggingface.co/RekaAI/reka-edge-2603&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;vLLM Reka plugin: &lt;a href="https://github.com/reka-ai/vllm-reka" rel="noopener noreferrer"&gt;github.com/reka-ai/vllm-reka&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Media Library app: &lt;a href="https://github.com/fboucher/media-library" rel="noopener noreferrer"&gt;github.com/fboucher/media-library&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Reka API platform: &lt;a href="https://platform.reka.ai" rel="noopener noreferrer"&gt;platform.reka.ai&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>vision</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Generating Short Clips from Long Videos with Python</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Tue, 24 Mar 2026 20:15:17 +0000</pubDate>
      <link>https://dev.to/reka/generating-short-clips-from-long-videos-with-python-33bj</link>
      <guid>https://dev.to/reka/generating-short-clips-from-long-videos-with-python-33bj</guid>
      <description>&lt;p&gt;This guide walks through &lt;code&gt;clip_generator.py&lt;/code&gt;, a script in the &lt;a href="https://github.com/reka-ai/clip-api-examples" rel="noopener noreferrer"&gt;clip-api-examples&lt;/a&gt; repo that uses the Clip API to extract short, captioned segments from a longer video. You provide a video source, either a YouTube URL or a local MP4 file, and the script handles the rest: downloading or uploading the video, analyzing it, and rendering a new clip based on your instructions. The example in this guide uses a YouTube URL to keep the setup simple.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The full source code is available at &lt;a href="https://github.com/reka-ai/clip-api-examples" rel="noopener noreferrer"&gt;github.com/reka-ai/clip-api-examples&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/bhq7Ws-w9Gk"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Clone the repository and install dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/reka-ai/clip-api-examples
&lt;span class="nb"&gt;cd &lt;/span&gt;clip-api-examples/python
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then export your API key:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REKA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_key_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't have a key yet, &lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;get one here&lt;/a&gt;. Free credits renew every month.&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%2Fwircrjxkdwkui0h4qbg8.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%2Fwircrjxkdwkui0h4qbg8.jpg" alt="The README file showing installation instructions and environmental variable setup"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Running the Script
&lt;/h2&gt;

&lt;p&gt;From the &lt;code&gt;python/&lt;/code&gt; folder, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python clip_generator.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script prompts you for a YouTube URL. Paste it in, and the job starts immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Job Lifecycle
&lt;/h2&gt;

&lt;p&gt;Once submitted, the job moves through several statuses printed to your terminal: &lt;code&gt;queued&lt;/code&gt;, &lt;code&gt;processing&lt;/code&gt;, then &lt;code&gt;completed&lt;/code&gt;. This works because the script uses &lt;code&gt;stream=True&lt;/code&gt; in its HTTP request, which keeps the connection open and lets the script poll in a loop until the job finishes. If you prefer, you can remove &lt;code&gt;stream=True&lt;/code&gt; and instead make one call to create the job, then a separate polling call to check its status.&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%2F62q1mh8aw30e1vc8qhdt.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%2F62q1mh8aw30e1vc8qhdt.jpg" alt="Terminal output showing the job status moving through various stages of processing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Customizing the Payload
&lt;/h2&gt;

&lt;p&gt;The heart of the script is the &lt;code&gt;create_clip&lt;/code&gt; function, which builds a payload for the API:&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="n"&gt;payload&lt;/span&gt; &lt;span class="o"&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;video_url&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&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;create an engaging video clip highlighting the best moments&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;template&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;moment&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;num_generations&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generation_config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;aspect_ratio&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;9:16&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;subtitles&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;min_duration_seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_duration_seconds&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each field is customizable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;prompt&lt;/code&gt;&lt;/strong&gt;: Plain-language instruction. Examples: "the funniest moments", "the part where I did the demo". Treat it like a brief to an editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;template&lt;/code&gt;&lt;/strong&gt;: Controls the overall clip style. &lt;code&gt;"moment"&lt;/code&gt; is one option; the repo includes others.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;num_generations&lt;/code&gt;&lt;/strong&gt;: Request multiple clip variations in one job, useful for comparing different cuts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;min_duration_seconds&lt;/code&gt; / &lt;code&gt;max_duration_seconds&lt;/code&gt;&lt;/strong&gt;: Keep these short during development for faster turnaround.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;subtitles&lt;/code&gt;&lt;/strong&gt;: Set to &lt;code&gt;True&lt;/code&gt; to burn captions into the video, &lt;code&gt;False&lt;/code&gt; to skip them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;aspect_ratio&lt;/code&gt;&lt;/strong&gt;: &lt;code&gt;"9:16"&lt;/code&gt; for TikTok/Reels, &lt;code&gt;"1:1"&lt;/code&gt; for square, or keep the original.&lt;/li&gt;
&lt;/ul&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%2Fmunvlaif0l9o2n0ijqn5.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%2Fmunvlaif0l9o2n0ijqn5.jpg" alt="A look at the Python code payload where aspect ratio and prompts are defined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Output
&lt;/h2&gt;

&lt;p&gt;When the job completes, the API returns a generated title, a description of the clip, and suggested hashtags. The video itself is already trimmed and has captions burned in. The demo in the video processed an 18-minute horizontal talk (no captions) into a 25-second vertical clip with orange subtitles.&lt;/p&gt;

&lt;p&gt;Clone the repo, adjust the payload to fit your content, and happy clipping.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>vision</category>
      <category>python</category>
    </item>
    <item>
      <title>Private Vision AI: Run Reka Edge Entirely on Your Machine</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Thu, 19 Mar 2026 13:17:39 +0000</pubDate>
      <link>https://dev.to/reka/private-vision-ai-run-reka-edge-entirely-on-your-machine-277b</link>
      <guid>https://dev.to/reka/private-vision-ai-run-reka-edge-entirely-on-your-machine-277b</guid>
      <description>&lt;p&gt;Reka just &lt;a href="https://reka.ai/news/reka-edge-frontier-level-edge-intelligence-for-physical-ai" rel="noopener noreferrer"&gt;released Reka Edge&lt;/a&gt;, a compact but powerful vision-language model that runs &lt;strong&gt;entirely on your own machine&lt;/strong&gt;. No API keys, no cloud, no data, leaving your computer. I work at Reka, and putting together this tutorial was genuinely fun; I hope you enjoy running it as much as I did.&lt;/p&gt;

&lt;p&gt;In three steps, you'll go from zero to asking an AI what's in any image or video.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You'll Need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A machine with enough RAM to run a 7B parameter model (~16 GB recommended)&lt;/li&gt;
&lt;li&gt;Git&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.astral.sh/uv/" rel="noopener noreferrer"&gt;&lt;code&gt;uv&lt;/code&gt;&lt;/a&gt;, a fast Python package manager. Install it with: &lt;code&gt;curl -LsSf https://astral.sh/uv/install.sh | sh&lt;/code&gt;. This works on macOS, Linux, and Windows (WSL). If you're on Windows without WSL, grab the &lt;a href="https://docs.astral.sh/uv/getting-started/installation/" rel="noopener noreferrer"&gt;Windows installer&lt;/a&gt; instead.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Get the Model and Inference Code
&lt;/h2&gt;

&lt;p&gt;Clone the Reka Edge repository from &lt;a href="https://huggingface.co" rel="noopener noreferrer"&gt;Hugging Face&lt;/a&gt;. This includes both the model weights and the inference code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://huggingface.co/RekaAI/reka-edge-2603
&lt;span class="nb"&gt;cd &lt;/span&gt;reka-edge-2603
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Grab a coffee while it downloads, the model weights are several GB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Fetch the Large Files
&lt;/h2&gt;

&lt;p&gt;Hugging Face stores large files (model weights and images) using Git LFS. After cloning, these files exist on disk but contain only small pointer files, not the actual content.&lt;/p&gt;

&lt;p&gt;First, make sure Git LFS is installed. The command varies by platform:&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="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;git-lfs

&lt;span class="c"&gt;# Linux / WSL (Ubuntu/Debian)&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git-lfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then initialize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git lfs &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then pull all large files, including model weights and media samples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git lfs pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3: Ask the Model About an Image or Video
&lt;/h2&gt;

&lt;p&gt;To analyze an image, use the sample included in the &lt;code&gt;media/&lt;/code&gt; folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run example.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--image&lt;/span&gt; ./media/hamburger.jpg &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"What is in this image?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or pass a video with &lt;code&gt;--video&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv run example.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--video&lt;/span&gt; ./media/many_penguins.mp4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--prompt&lt;/span&gt; &lt;span class="s2"&gt;"What is in this?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model will load, process your input, and print a description, all locally, all private.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try different prompts to unlock more:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;"Describe this scene in detail."&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"What text is visible in this image?"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;"Is there anything unusual or unexpected here?"&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Actually Happening? &lt;em&gt;(optional reading)&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;You don't need this to use the model, but if you're anything like me and can't help wondering what's going on under the hood, here's the magic behind &lt;code&gt;example.py&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. It picks the best hardware available.&lt;/strong&gt;&lt;br&gt;
The script checks whether your machine has a GPU (CUDA for Nvidia, Metal for Apple Silicon) and uses it automatically. If neither is available, it falls back to the CPU. This affects speed, not quality.&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_available&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&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;device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cuda&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;mps_ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&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;device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mps&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="o"&gt;=&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;device&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cpu&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;&lt;strong&gt;2. It loads the model into memory.&lt;/strong&gt;&lt;br&gt;
The 7 billion parameter model is read from the folder you cloned. This is the "weights": billions of numbers that encode everything the model has learned. Loading takes ~30 seconds depending on your hardware.&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="n"&gt;processor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoProcessor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trust_remote_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoModelForImageTextToText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...).&lt;/span&gt;&lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. It packages your input into a structured message.&lt;/strong&gt;&lt;br&gt;
Your image (or video) and your text prompt are wrapped together into a conversation-style format, the same way a chat message works, except one part is visual instead of text.&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="n"&gt;messages&lt;/span&gt; &lt;span class="o"&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;role&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;user&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;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;type&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;image&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;image&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&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;type&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;text&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;text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. It converts everything into numbers.&lt;/strong&gt;&lt;br&gt;
The processor translates your image into a grid of numerical patches and your prompt into tokens (small chunks of text, each mapped to a number). The model only understands numbers, so this step bridges the gap.&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="n"&gt;inputs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply_chat_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokenize&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_tensors&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_dict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. The model generates a response, token by token.&lt;/strong&gt;&lt;br&gt;
Starting from your input, the model predicts the most likely next word, then the next, up to 256 tokens. It stops when it hits a natural end-of-response marker.&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="n"&gt;output_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_new_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;do_sample&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ftz4ujni9y0hssgkmc45j.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%2Ftz4ujni9y0hssgkmc45j.png" alt="The prompt and the burger image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. It converts the numbers back into text and prints it.&lt;/strong&gt;&lt;br&gt;
The token IDs are decoded back into human-readable words and printed to your terminal. No internet involved at any point.&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="n"&gt;output_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;processor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skip_special_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Here the video
&lt;/h2&gt;

&lt;p&gt;If you prefer watching and reading, here is the video version: &lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/QVYa4jBwH-s"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  That's Pretty Cool, Right?
&lt;/h2&gt;

&lt;p&gt;A single script. No API key. No cloud. You just ran a 7 billion parameter vision-language model entirely on your own machine, and it works whether you're on a Mac, Linux, or Windows with WSL, which is what I was using when I wrote this.&lt;/p&gt;

&lt;p&gt;This works great as a one-off script: drop in a file, ask a question, get an answer. But what if you wanted to build something on top of it? A web app, a tool that watches a folder, or anything that needs to talk to the model repeatedly?&lt;/p&gt;

&lt;p&gt;That's exactly what the next post is about. I'll show you how to wrap Edge as a local API, so instead of running a script, you have a service running on your machine that any app can plug into. Same model, same privacy, but now it's a proper building block.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>development</category>
      <category>python</category>
      <category>vision</category>
    </item>
    <item>
      <title>Automatically Create AI Clips with This n8n Template</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Thu, 29 Jan 2026 21:10:41 +0000</pubDate>
      <link>https://dev.to/reka/automatically-create-ai-clips-with-this-n8n-template-6c1</link>
      <guid>https://dev.to/reka/automatically-create-ai-clips-with-this-n8n-template-6c1</guid>
      <description>&lt;p&gt;I'm excited to share that my new n8n template has been approved and is now available for everyone to use! This template automates the process of creating AI-generated video clips from YouTube videos and sending notifications directly to your inbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try the template here:&lt;/strong&gt; &lt;a href="https://link.reka.ai/n8n-template-api" rel="noopener noreferrer"&gt;https://link.reka.ai/n8n-template-api&lt;/a&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%2Ft6p6nnxgazvbr9d7w1kn.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%2Ft6p6nnxgazvbr9d7w1kn.png" alt="New Reka's n8n clipping template"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does This Template Do?
&lt;/h2&gt;

&lt;p&gt;If you've ever wanted to automatically create short clips from long YouTube videos, this template is for you. It watches a YouTube channel of your choice, and whenever a new video is published, it uses AI to generate engaging short clips perfect for social media. You get notified by email when your clip is ready to download.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The workflow is straightforward and runs completely on autopilot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor YouTube channels&lt;/strong&gt; - The template watches the RSS feed of any YouTube channel you specify. When a new video appears, the automation kicks off.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Request AI clip generation&lt;/strong&gt; - Using Reka's Vision API, the workflow sends the video for AI processing. You have full control over the output:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a custom prompt to guide the AI on what kind of clip to create&lt;/li&gt;
&lt;li&gt;Choose whether to include captions&lt;/li&gt;
&lt;li&gt;Set minimum and maximum clip duration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Smart status checking&lt;/strong&gt; - When the clips are ready, you receive a success email with your download link. As a safety feature, if the job takes too long, you'll get an error notification instead.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started is Easy
&lt;/h2&gt;

&lt;p&gt;The best part? You can install this template with just one click from the &lt;a href="https://link.reka.ai/n8n-template-api" rel="noopener noreferrer"&gt;n8n Templates page&lt;/a&gt;. No complex setup required!&lt;/p&gt;

&lt;p&gt;After installation, you'll just need two quick things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A free Reka AI API key (&lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;get yours from Reka&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A Gmail account (or use any email provider you like)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's it! The template comes ready to use. Simply add your YouTube channel RSS feed, connect your API key, and you're ready to start generating clips automatically. The whole setup takes just a few minutes.&lt;/p&gt;

&lt;p&gt;If you run into any questions or want to share what you've built, join the &lt;a href="https://link.reka.ai/discord" rel="noopener noreferrer"&gt;Reka Discord community&lt;/a&gt;. I'd love to hear how you're using this template!&lt;/p&gt;

&lt;h2&gt;
  
  
  Show Me
&lt;/h2&gt;

&lt;p&gt;In this short video, I show you how to get that template into your n8n and how to configure it.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/DASEXKY86u0"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;Happy clipping!&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>lowcode</category>
      <category>nocode</category>
    </item>
    <item>
      <title>Exposing Home Container with Traefik and Cloudflare Tunnel</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Wed, 07 Jan 2026 12:48:00 +0000</pubDate>
      <link>https://dev.to/fboucheros/exposing-home-container-with-traefik-and-cloudflare-tunnel-411n</link>
      <guid>https://dev.to/fboucheros/exposing-home-container-with-traefik-and-cloudflare-tunnel-411n</guid>
      <description>&lt;p&gt;I love the cloud, in fact most people probably know me because of my shared content related to that. But sometimes our apps don't need scaling, or redundancy. Sometimes we just want to host them somewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.cloudenfrancais.com/posts/2026-01-07-treafik-cloudflare-setup-fr.html" rel="noopener noreferrer"&gt;French version available here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was the holidays, and during my time off I worked on a few small personal projects. I packaged them in containers so it's easy to deploy anywhere. I deployed them on a mini-PC that I have at home and it is great... as long as I stay home. But what if I would like to access it from elsewhere (ex: my in-laws' house)?&lt;/p&gt;

&lt;p&gt;I set up a nice Cloudflare tunnel to a Traefik container that proxies the traffic to the correct container based on the prefix or second-level domain. So &lt;code&gt;dev.c5m.ca&lt;/code&gt; goes to container X and &lt;code&gt;test.c5m.ca&lt;/code&gt; goes to container Y. In this post, I wanted to share how I did it (and also have it somewhere for me in case I need to do it again 😉). It's simple once you know all the pieces work together.&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%2Fuhxsxqfw5r3siq9q7ihg.jpeg" 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%2Fuhxsxqfw5r3siq9q7ihg.jpeg" alt="beam of light from a window light a container" width="800" height="457"&gt;&lt;/a&gt;&lt;em&gt;image generated by Microsoft designer&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;The architecture is straightforward: Cloudflare Tunnel creates a secure connection from my home network to Cloudflare's edge, and Traefik acts as a reverse proxy that routes dynamically incoming requests to the appropriate container based on the subdomain. This way, I can access multi ple services through different subdomains without exposing my home network directly to the internet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Cloudflare Tunnel
&lt;/h2&gt;

&lt;p&gt;First, assuming you already owne a domain name, you'll need to create a Cloudflare tunnel. You can do this through the Cloudflare dashboard under Zero Trust → Networks → Tunnels. Once created, you'll get a tunnel token that you'll use in the configuration.&lt;/p&gt;

&lt;p&gt;Here's my &lt;code&gt;cloudflare-docker-compose.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: cloudflare-tunnel

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    restart: unless-stopped
    env_file:
      - .env
    environment:
      - TUNNEL_TOKEN=${TUNNEL_TOKEN}
    command: ["tunnel", "--no-autoupdate", "run", "--token", "${TUNNEL_TOKEN}"]

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

&lt;/div&gt;



&lt;p&gt;The tunnel token is stored in a &lt;code&gt;.env&lt;/code&gt; file for security. The &lt;code&gt;--no-autoupdate&lt;/code&gt; flag prevents the container from trying to update itself automatically, which is useful in a controlled environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: DNS Configuration
&lt;/h2&gt;

&lt;p&gt;In Cloudflare dashboard, create a &lt;code&gt;CNAME&lt;/code&gt; Record with a wildcard. For example for my domain "c5m.ca" that record will look like this: &lt;code&gt;*.c5m.ca&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Traefik Configuration
&lt;/h2&gt;

&lt;p&gt;Traefik is the reverse proxy that will route traffic to your containers. I have two configuration files: one for Traefik itself and one for the Docker Compose setup.&lt;/p&gt;

&lt;p&gt;Here's my &lt;code&gt;traefik.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;global:
  checkNewVersion: false
  sendAnonymousUsage: false

api:
  dashboard: false #true
  insecure: true

entryPoints:
  web:
    address: :8082
  websecure:
    address: :8043

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false 

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

&lt;/div&gt;



&lt;p&gt;I've configured two entry points: &lt;code&gt;web&lt;/code&gt; on port 8082 (HTTP) and &lt;code&gt;websecure&lt;/code&gt; on port 8043 (HTTPS). I did it that way because the default 80 and 443 where already taken. The Docker provider watches for containers with Traefik labels and automatically configures routing. &lt;code&gt;exposedByDefault: false&lt;/code&gt; means containers won't be exposed unless explicitly enabled with labels. You won't have to change Traefik config to add more containers, it's all dynamic.&lt;/p&gt;

&lt;p&gt;And here's the &lt;code&gt;traefik-docker-compose.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: traefik

services:
  traefik:
    image: "traefik:v3.4"
    container_name: "traefik-app"
    restart: unless-stopped
    networks:
      - proxy

    ports:
      - "8888:8080" # Dashboard port
      - "8082:8082"
      - "8043:8043" # remap 443
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./config/traefik.yaml:/etc/traefik/traefik.yaml:ro"

networks:
  proxy:
    name: proxy

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

&lt;/div&gt;



&lt;p&gt;The key points here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Traefik is connected to a Docker network called &lt;code&gt;proxy&lt;/code&gt; that will be shared with other containers. You can name it the way you like.&lt;/li&gt;
&lt;li&gt;Port 8888 maps to Traefik's dashboard (currently disabled in the config)&lt;/li&gt;
&lt;li&gt;Ports 8082 and 8043 are exposed for HTTP and HTTPS traffic&lt;/li&gt;
&lt;li&gt;The Docker socket is mounted read-only so Traefik can discover containers&lt;/li&gt;
&lt;li&gt;The configuration file is mounted from &lt;code&gt;./config/traefik.yaml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 4: Configuring Services
&lt;/h2&gt;

&lt;p&gt;Now, any container you want to expose through Traefik needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Be on the same &lt;code&gt;proxy&lt;/code&gt; network&lt;/li&gt;
&lt;li&gt;Have Traefik labels configured&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a simple example with an nginx container (&lt;code&gt;nginx-docker-compose.yaml&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: "test-tools"

services:
  nginx:
    image: "nginx:latest"
    container_name: "nginx-test"
    restart: unless-stopped
    networks:
      - proxy
    volumes:
      - "./html:/usr/share/nginx/html:ro"

    labels:
      - traefik.enable=true
      - traefik.http.routers.nginxtest.rule=Host(`test.c5m.ca`) 
      - traefik.http.routers.nginxtest.entrypoints=web

networks:
  proxy:
    external: true

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

&lt;/div&gt;



&lt;p&gt;The labels tell Traefik:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;traefik.enable=true&lt;/code&gt;: This container should be exposed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nginxtest&lt;/code&gt; is the &lt;strong&gt;unique&lt;/strong&gt; name for routing this container.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traefik.http.routers.nginxtest.rule=Host(...)&lt;/code&gt;: Route requests for &lt;code&gt;test.c5m.ca&lt;/code&gt; to this container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traefik.http.routers.nginxtest.entrypoints=web&lt;/code&gt;: Use the &lt;code&gt;web&lt;/code&gt; entry point (port 8082)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Bonus: A More Complex Example
&lt;/h2&gt;

&lt;p&gt;For a more realistic scenario, let's share how I could expose &lt;a href="https://github.com/FBoucher/2d6-dungeon-app" rel="noopener noreferrer"&gt;2D6 Dungeon App&lt;/a&gt;here's a simplified version of my &lt;code&gt;2d6-docker-compose.yaml&lt;/code&gt; which includes a multi-container application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name: 2d6-dungeon

services:
  database:
    container_name: 2d6_db
    ports:
      - "${MYSQL_PORT:-3306}:3306"
    networks:
      - proxy
    ...

  dab:
    container_name: 2d6_dab
    ...
    depends_on:
      database:
        condition: service_healthy
    ports:
      - "${DAB_PORT:-5000}:5000"
    networks:
      - proxy

  webapp:
    container_name: 2d6_app
    depends_on:
      - dab
    environment:
      ConnectionStrings__dab: http://dab:5000
      services __dab__ http__0: http://dab:5000

    labels:
      - traefik.enable=true
      - traefik.http.routers.twodsix.rule=Host(`2d6.c5m.ca`)
      - traefik.http.routers.twodsix.entrypoints=web,websecure
      - traefik.http.services.twodsix.loadbalancer.server.port=${WEBAPP_PORT:-8080}

    networks:
      - proxy

    ports:
      - "${WEBAPP_PORT:-8080}:${WEBAPP_PORT:-8080}"

networks:
  proxy:
    external: true

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

&lt;/div&gt;



&lt;p&gt;This example shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple services working together (database, API, web app)&lt;/li&gt;
&lt;li&gt;Only the webapp is exposed through Traefik (the database and API are internal)&lt;/li&gt;
&lt;li&gt;The webapp uses both &lt;code&gt;web&lt;/code&gt; and &lt;code&gt;websecure&lt;/code&gt; entry points&lt;/li&gt;
&lt;li&gt;Important note here is that container part of the same network can use their internal port (ex: 5000 for DAB, 3306 for MySQL)&lt;/li&gt;
&lt;li&gt;The external network is the &lt;code&gt;proxy&lt;/code&gt; created previously&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cloudflare Tunnel Configuration
&lt;/h2&gt;

&lt;p&gt;In your Cloudflare dashboard, you'll need to configure the tunnel to route traffic to Traefik. Create a public hostname that points to &lt;code&gt;http://&amp;lt;local-ip&amp;gt;:8082&lt;/code&gt;. Use the local IP of your server something like "192.168.1.123" You can use wildcards like &lt;code&gt;*.c5m.ca&lt;/code&gt; to route all subdomains to Traefik, which will then handle the routing based on the hostname.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;That's it! Once everything is set up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-f1dT-tkVmoVGhJUNrG_D26IhhlNQdw3GYsyxzdAeGaqTgIsHXX0CGCHD5ge1vj7pr_oYKkVkW5S0z1Z1bQcs6dRqVHV_JryDI_LZVF-XnFyGX6MiXFcVwNHCGl6-GYFvU7DuktGHxmhTPUhovWtBjLWF7N0xp2OdYBN4BLKNKIZ0TQhFBNoOqUfRcxs/s702/cloudflare-traefik.png" rel="noopener noreferrer"&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%2Fvs6iuhqo5frhmqjn073m.png" alt="architecture diagram" width="638" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Cloudflare tunnel creates a secure connection from your home to Cloudflare&lt;/li&gt;
&lt;li&gt;Traffic comes in through Cloudflare and gets routed to Traefik&lt;/li&gt;
&lt;li&gt;Traefik reads the hostname and routes to the appropriate container&lt;/li&gt;
&lt;li&gt;Each service can be accessed via its own subdomain&lt;/li&gt;
&lt;li&gt;Only the containers with the Traefik labels are accessible from outside my network&lt;/li&gt;
&lt;li&gt;It's dynamic! Any new container, with the labels, will be routed without changing the config in Traefik nor Cloudflare&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It's a simple setup that works great for personal projects. The best part is that you don't need to expose any ports on your router or deal with dynamic DNS, Cloudflare handles all of that.&lt;/p&gt;

&lt;p&gt;Next step will be to add some authentication and authorization (ex: using Keycloak), but that's for another post. For now, this gives me a way to access my home-hosted services from anywhere, and I thought it could be useful to share.&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>container</category>
      <category>homelab</category>
      <category>post</category>
    </item>
    <item>
      <title>From Hours to Minutes: AI That Finds Tech Events for You</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Mon, 05 Jan 2026 08:30:00 +0000</pubDate>
      <link>https://dev.to/fboucheros/from-hours-to-minutes-ai-that-finds-tech-events-for-you-33g7</link>
      <guid>https://dev.to/fboucheros/from-hours-to-minutes-ai-that-finds-tech-events-for-you-33g7</guid>
      <description>&lt;h4&gt;
  
  
  TL;DR
&lt;/h4&gt;

&lt;p&gt;I built an AI research agent that actually browses the live web and finds tech events, no search loops, no retry logic, no hallucinations. Just ask a question and get structured JSON back with the reasoning steps included. The secret? An API that handles multi-step research automatically. Built with .NET/Blazor in a weekend. &lt;a href="https://www.youtube.com/watch?v=ML-9SrQm2Dk" rel="noopener noreferrer"&gt;Watch me build it&lt;/a&gt; | &lt;a href="https://link.reka.ai/event-finder-dotnet" rel="noopener noreferrer"&gt;Get the code&lt;/a&gt; | &lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;Free API key&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy New Year! I wanted to share something I recently presented at the AI Agents Conference 2025: how to build intelligent research assistants that can search the live web and return structured, reliable results.&lt;/p&gt;

&lt;p&gt;Coming back from the holidays, I'm reminded of a universal problem: information overload. Whether it's finding relevant tech conferences, catching up on industry news, or wading through piles of documentation that accumulated during time off, we all need tools that can quickly search and synthesize information for us. That's what Reka Research does it's an agentic AI that browses the web (or your private documents), answers complex questions, and turns hours of research into minutes. I built a practical demo to show this in action: an Event Finder that searches the live internet for upcoming tech conferences.&lt;/p&gt;

&lt;p&gt;The full presentation is available on YouTube if you want to follow along: &lt;a href="https://www.youtube.com/watch?v=ML-9SrQm2Dk" rel="noopener noreferrer"&gt;How to Build Agentic Web Research Assistants&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Finding Events Isn't Just a Simple Search
&lt;/h2&gt;

&lt;p&gt;Let me paint a picture. You want to find upcoming tech conferences about AI in your area. You need specific information: the event name, start and end dates, location, and most importantly, the registration URL.&lt;/p&gt;

&lt;p&gt;A simple web search or basic LLM query falls short because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You might get outdated information&lt;/li&gt;
&lt;li&gt;The first search result rarely contains all required details&lt;/li&gt;
&lt;li&gt;You need to cross-reference multiple sources&lt;/li&gt;
&lt;li&gt;Without structure, the data is hard to use in an application&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where Reka's Research API shines. It doesn't just search it reasons through multiple steps, aggregates information, and returns structured, grounded results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhY5pBoGJFBSgPEhZBqDdLeVmaqBktb4d6jbJbfRHgJb04qW-KtcQcLcbAq1Hv8dbF56_n_biCbqSxjy_lB6qIApRb-kq4HzjFCG3PsG7-Xu-w05hd1CYlghYfFrXZCuOgRMg3Cx5f3uKiEdFgFC0U0y0mq4DFGzF4XLkcuVc1m7RNpBrOCae9GD3kAg2Y/s3090/events_finder.png" rel="noopener noreferrer"&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%2Fqqsnirlkqfrdjno9l36d.png" alt="Event finder interface" width="400" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Multi-Step Research That Actually Works
&lt;/h2&gt;

&lt;p&gt;The core innovation here is multi-step grounding. Instead of making a single query and hoping for the best, the Research API acts like a diligent human researcher:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It makes an initial search based on your query&lt;/li&gt;
&lt;li&gt;Checks what information is missing&lt;/li&gt;
&lt;li&gt;Performs additional targeted searches&lt;/li&gt;
&lt;li&gt;Aggregates and validates the data&lt;/li&gt;
&lt;li&gt;Returns a complete, structured response&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;As a developer, you simply send your question, and the API handles the complex iteration. No need to build your own search loops or retry logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works: The Developer Experience
&lt;/h2&gt;

&lt;p&gt;Here's what surprised me most: the simplicity. You define your data structure, ask a question, and the API handles all the complex research orchestration. No retry logic, no search loop management.&lt;/p&gt;

&lt;p&gt;The key is structured output. Instead of parsing messy text, you tell the API exactly what JSON schema you want:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class TechEvent
{
    public string? Name { get; set; }
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
    public string? City { get; set; }
    public string? Country { get; set; }
    public string? Url { get; set; }
}

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

&lt;/div&gt;



&lt;p&gt;Then you send your query with the schema, and it returns perfectly structured data every time. The API uses OpenAI compatible format, so if you've worked with ChatGPT's API, this feels instantly familiar.&lt;/p&gt;

&lt;p&gt;The real magic? You also get back the reasoning steps the actual web searches it performed and how it arrived at the answer. Perfect for debugging and understanding the agent's thought process.&lt;/p&gt;

&lt;p&gt;I walk through the complete implementation, including domain filtering, location aware search, and handling the async research calls in the &lt;a href="https://www.youtube.com/watch?v=ML-9SrQm2Dk" rel="noopener noreferrer"&gt;video&lt;/a&gt;. The &lt;a href="https://link.reka.ai/event-finder-dotnet" rel="noopener noreferrer"&gt;full source code&lt;/a&gt; is on GitHub if you want to dive deeper.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=ML-9SrQm2Dk" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogger.googleusercontent.com%2Fimg%2Fa%2FAVvXsEhpMSSsyyxh7SqLiBpJvk_gNDywmSXIFRry46hMXvSnJpRbgnVxJrVVOg6Snrz63kAcQ-Uv3nt7KHqwf9ImcQnrcfDNwrbcpuZgeN72ogJDuxlfTcNpdZe7NDUBTezH9fibGF9cxq8nI6rRDzs9nESLvfCn-D8HyKFFseWgg5rJpIzIB7LbMaPcVKYmNA0%3Dw400-h224" width="400" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://link.reka.ai/event-finder-dotnet" rel="noopener noreferrer"&gt;complete source code&lt;/a&gt; is on GitHub. Clone it, grab a &lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;free API key&lt;/a&gt;, and you'll have it running in under 5 minutes.&lt;/p&gt;

&lt;p&gt;I'm curious what you'll build with this. Research agents that monitor news? Product comparison tools? Documentation synthesizers? The API works for any web research task. If you build something, tag me.  I'd love to see it.&lt;/p&gt;

&lt;p&gt;Happy New Year! 🎉&lt;/p&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>webdev</category>
      <category>github</category>
    </item>
    <item>
      <title>Ask AI from Anywhere: No GUI, No Heavy Clients, No Friction</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Wed, 26 Nov 2025 16:33:00 +0000</pubDate>
      <link>https://dev.to/fboucheros/ask-ai-from-anywhere-no-gui-no-heavy-clients-no-friction-23k2</link>
      <guid>https://dev.to/fboucheros/ask-ai-from-anywhere-no-gui-no-heavy-clients-no-friction-23k2</guid>
      <description>&lt;p&gt;Ever wished you could ask AI from anywhere without needing an interface? Imagine just typing &lt;code&gt;?&lt;/code&gt; and your question in any terminal the moment it pops into your head, and getting the answer right away! In this post, I explain how I wrote a tiny shell script that turns this idea into reality, transforming the terminal into a universal AI client. You can query Reka, OpenAI, or a local Ollama model from any editor, tab, or pipeline—no GUI, no heavy clients, no friction.&lt;/p&gt;

&lt;p&gt;Small, lightweight, and surprisingly powerful: once you make it part of your workflow, it becomes indispensable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💡 All the code scripts are available at:&lt;/strong&gt; &lt;a href="https://github.com/reka-ai/terminal-tools" rel="noopener noreferrer"&gt;https://github.com/reka-ai/terminal-tools&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3wG__5sSh3sB2Vn-MZBPaReftp34Ns0In_NAjXL_4URVuZK89lLgJnAP3GTfuRsJnYI2dxq7WsgR9bVzFQXVWbL7UzlVy-00i76T6qnaXmWqi-9JaTonib3DxSG9ib_W9cDRGYzIelIvyeEUqJWXRm3CK-Q1e9ecRAHr0f6aUFQ9SppBbeuILiQMr5yY/s800/quick-ask_800.gif" rel="noopener noreferrer"&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%2F7l4g9ifd8ug549j82aqb.gif" alt="the hat script in action"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;There is almost always a terminal within reach—embedded in your editor, sitting in a spare tab, or already where you live while building, debugging, and piping data around. So why break your flow to open a separate chat UI? I wanted to just type a single character (&lt;code&gt;?&lt;/code&gt;) plus my question and get an answer right there. No window hopping. No heavy client.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works
&lt;/h2&gt;

&lt;p&gt;The trick is delightfully small: send a single JSON POST request to whichever AI provider you feel like (Reka, OpenAI, Ollama locally, etc.):&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="c"&gt;# Example: Reka&lt;/span&gt;
curl https://api.reka.ai/v1/chat
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &amp;lt;API_KEY&amp;gt;"&lt;/span&gt; 
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"messages"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
            &lt;span class="o"&gt;{&lt;/span&gt;
              &lt;span class="s2"&gt;"role"&lt;/span&gt;: &lt;span class="s2"&gt;"user"&lt;/span&gt;,
              &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"What is the origin of thanksgiving?"&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
          &lt;span class="o"&gt;]&lt;/span&gt;,
          &lt;span class="s2"&gt;"model"&lt;/span&gt;: &lt;span class="s2"&gt;"reka-core"&lt;/span&gt;,
          &lt;span class="s2"&gt;"stream"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;


&lt;span class="c"&gt;# Example: Ollama local&lt;/span&gt;
curl http://127.0.0.1:11434/api/chat 
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;  
      &lt;span class="s2"&gt;"model"&lt;/span&gt;: &lt;span class="s2"&gt;"llama3"&lt;/span&gt;,   
      &lt;span class="s2"&gt;"messages"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
        &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"role"&lt;/span&gt;: &lt;span class="s2"&gt;"user"&lt;/span&gt;, 
          &lt;span class="s2"&gt;"content"&lt;/span&gt;: &lt;span class="s2"&gt;"What is the origin of thanksgiving?"&lt;/span&gt;
        &lt;span class="o"&gt;}]&lt;/span&gt;, 
      &lt;span class="s2"&gt;"stream"&lt;/span&gt;: &lt;span class="nb"&gt;false&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Once we get the response, we extract the answer field from it. A thin shell wrapper turns that into a universal “ask” verb for your terminal. Add a short alias (&lt;code&gt;?&lt;/code&gt;) and you have the most minimalist AI client imaginable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's go into the details
&lt;/h2&gt;

&lt;p&gt;Let me walk you through the core script step-by-step using &lt;code&gt;reka-chat.sh&lt;/code&gt;, so you can customize it the way you like. Maybe this is a good moment to mention that Reka has a &lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;free tier&lt;/a&gt; that's more than enough for this. Go grab your key—after all, it's free!&lt;/p&gt;

&lt;p&gt;The script (&lt;code&gt;reka-chat.sh&lt;/code&gt;) does four things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Captures your question&lt;/li&gt;
&lt;li&gt;Loads an API key from &lt;code&gt;~/.config/reka/api_key&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sends a JSON payload to the chat endpoint with &lt;code&gt;curl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Extracts the answer using &lt;code&gt;jq&lt;/code&gt; for clean plain text.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Capture Your Question
&lt;/h3&gt;

&lt;p&gt;This part of the script is a pure laziness hack. I wanted to save keystrokes by not requiring quotes when passing a question as an argument. So &lt;code&gt;? What is 32C in F&lt;/code&gt; works just as well as &lt;code&gt;? "What is 32C in F"&lt;/code&gt;.&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="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;$# &lt;/span&gt;&lt;span class="nt"&gt;-eq&lt;/span&gt; 0]&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    if&lt;/span&gt; &lt;span class="o"&gt;[!&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; 0]&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nv"&gt;QUERY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
else
    &lt;/span&gt;&lt;span class="nv"&gt;QUERY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&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="k"&gt;fi&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Load Your API Key
&lt;/h3&gt;

&lt;p&gt;If you're running Ollama locally you don't need any key, but for all other AI providers you do. I store mine in a locked-down file at &lt;code&gt;~/.config/reka/api_key&lt;/code&gt;, then read and trim trailing whitespace like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY_FILE="$HOME/.config/reka/api_key"
API_KEY=$(cat "$API_KEY_FILE" | tr -d '[:space:]')

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Send The JSON Payload
&lt;/h3&gt;

&lt;p&gt;Building the JSON payload is the heart of the script, including the &lt;code&gt;API_ENDPOINT&lt;/code&gt;, &lt;code&gt;API_KEY&lt;/code&gt;, and obviously our &lt;code&gt;QUERY&lt;/code&gt;. Here’s how I do it for Reka:&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;RESPONSE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$API_ENDPOINT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-Api-Key: &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
     &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{
  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;messages&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: [
    {
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
      &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;content&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$QUERY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-R&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; .&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;
    }
  ],
  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;model&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;reka-core&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;,
  &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;stream&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: false
}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Extract The Answer
&lt;/h3&gt;

&lt;p&gt;Finally, we parse the JSON response with &lt;code&gt;jq&lt;/code&gt; to pull out just the answer text. If &lt;code&gt;jq&lt;/code&gt; isn't installed we display the raw response, but a formatted answer is much nicer. If you are customizing for another provider, you may need to adjust the JSON path here. You can add &lt;code&gt;echo "$RESPONSE" &amp;gt;&amp;gt; data_sample.json&lt;/code&gt; to the script to log raw responses for tinkering.&lt;/p&gt;

&lt;p&gt;With Reka, the response look like this:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cb7c371b-3a7b-48d2-829d-70ffacf565c6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"reka-core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"usage"&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;"input_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"output_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;460&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"reasoning_tokens"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&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;"responses"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"finish_reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"message"&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;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assistant"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" The origin of Thanksgiving ..."&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="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="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;The value we are looking for and want to display is the &lt;code&gt;content&lt;/code&gt; field inside &lt;code&gt;responses[0].message&lt;/code&gt;. Using &lt;code&gt;jq&lt;/code&gt;, we do:&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$RESPONSE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.responses[0].message.content // .error // "Error: Unexpected response format"'&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Now that we have the script, make it executable with &lt;code&gt;chmod +x reka-chat.sh&lt;/code&gt;, and let's add an alias to your shell config to make it super easy to use. Add one line to your &lt;code&gt;.zshrc&lt;/code&gt; or &lt;code&gt;.bashrc&lt;/code&gt; that looks like this:&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="nb"&gt;alias&lt;/span&gt; &lt;span class="se"&gt;\\&lt;/span&gt;?&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$REKA_CHAT_SCRIPT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;?&lt;/code&gt; is a special character in the shell, we escape it with a backslash. After adding this line, reload your shell configuration with &lt;code&gt;source ~/.zshrc&lt;/code&gt; or &lt;code&gt;source ~/.bashrc&lt;/code&gt;, and you are all set!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Result
&lt;/h2&gt;

&lt;p&gt;Now you can ask questions directly from your terminal. Wanna know what is origin of Thanksgiving, ask it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;? What is the origin of Thanksgiving

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

&lt;/div&gt;



&lt;p&gt;And if you want to keep the quotes, please you do you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Extra: Web research
&lt;/h2&gt;

&lt;p&gt;I couldn't stop there! Reka also supports &lt;strong&gt;web research&lt;/strong&gt; , which means it can fetch and read web pages to provide more informed answers. Following the same pattern described previously, I wrote a similar script called &lt;code&gt;reka-research.sh&lt;/code&gt; that sends a request to Reka's research endpoint. This obviously takes a bit more time to answer, as it's making different web queries and processing them, but the results are often worth the wait—and they are up to date! I used the alias &lt;code&gt;??&lt;/code&gt; for this one.&lt;/p&gt;

&lt;p&gt;On the &lt;a href="https://github.com/reka-ai/terminal-tools" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, you can find both scripts (&lt;code&gt;reka-chat.sh&lt;/code&gt; and &lt;code&gt;reka-research.sh&lt;/code&gt;) along with a script to create the aliases automatically. Feel free to customize them to fit your workflow and preferred AI provider. Enjoy the newfound superpower of instant AI access right from your terminal!&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;With this setup, the possibilities are endless. Reka supports questions related to audio and video, which could be interesting to explore next. The project is open source, so feel free to contribute or suggest improvements. You can also join the Reka community on &lt;a href="https://link.reka.ai/discord" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; to share your experiences and learn from others.&lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/nugPkWLUOZQ"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/reka-ai/terminal-tools" rel="noopener noreferrer"&gt;Reka Terminal Tools on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;Reka FREE API key&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jqlang.org/" rel="noopener noreferrer"&gt;jq - Command-line JSON processor&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>tools</category>
      <category>linux</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Check-In Doc MCP Server: A Handy Way to Search Only the Docs You Trust</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Tue, 18 Nov 2025 10:30:00 +0000</pubDate>
      <link>https://dev.to/fboucheros/check-in-doc-mcp-server-a-handy-way-to-search-only-the-docs-you-trust-4dl6</link>
      <guid>https://dev.to/fboucheros/check-in-doc-mcp-server-a-handy-way-to-search-only-the-docs-you-trust-4dl6</guid>
      <description>&lt;p&gt;Ever wished you could ask a question and have the answer come only from a handful of trusted documentation sites—no random blogs, no stale forum posts? That’s exactly what the &lt;strong&gt;Check-In Doc MCP Server&lt;/strong&gt; does. It’s a lightweight Model Context Protocol (MCP) server you can run locally (or host) to funnel questions to selected documentation domains and get a clean AI-generated answer back.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  What It Is
&lt;/h2&gt;

&lt;p&gt;The project (GitHub: &lt;a href="https://github.com/fboucher/check-in-doc-mcp" rel="noopener noreferrer"&gt;https://github.com/fboucher/check-in-doc-mcp&lt;/a&gt;) is a Dockerized MCP server that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accepts a user question.&lt;/li&gt;
&lt;li&gt;Calls the &lt;strong&gt;Reka AI Research API&lt;/strong&gt; with constraints (only &lt;em&gt;allowed&lt;/em&gt; domains).&lt;/li&gt;
&lt;li&gt;Returns a synthesized answer based on live documentation retrieval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You control which sites are searchable by passing a comma‑separated list of domains (e.g. &lt;code&gt;docs.reka.ai,docs.github.com&lt;/code&gt;). That keeps &amp;gt; results focused, reliable, and relevant.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Is the Reka AI Research API?
&lt;/h2&gt;

&lt;p&gt;Reka AI’s &lt;a href="https://docs.reka.ai/research" rel="noopener noreferrer"&gt;Research API&lt;/a&gt; lets you blend language model reasoning with targeted, on‑the‑fly web/document retrieval. Instead of a model hallucinating an answer from static training data, it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perform limited domain‑scoped web searches.&lt;/li&gt;
&lt;li&gt;Pull fresh snippets.&lt;/li&gt;
&lt;li&gt;Integrate them into a structured response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this project, we use the research feature with a &lt;code&gt;web_search&lt;/code&gt; block specifying:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;allowed_domains&lt;/code&gt;: Only the documentation sites you trust.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_uses&lt;/code&gt;: Caps how many retrieval calls it makes per query (controls cost &amp;amp; latency).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Details used here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; &lt;code&gt;reka-flash-research&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Endpoint:&lt;/strong&gt; &lt;code&gt;http://api.reka.ai/v1/chat/completions&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auth:&lt;/strong&gt; Bearer API key (generated from the Reka dashboard: &lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;https://link.reka.ai/free&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How It Works Internally
&lt;/h2&gt;

&lt;p&gt;The core logic lives in &lt;code&gt;ResearchService&lt;/code&gt; (&lt;code&gt;src/Domain/ResearchService.cs&lt;/code&gt;). Simplified flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initialization&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Stores the API key + array of allowed domains, sets model &amp;amp; endpoint, logs a safe startup message.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build Request Payload&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
The &lt;code&gt;CheckInDoc(string question)&lt;/code&gt; method creates a JSON payload:&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Send Request&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Creates a &lt;code&gt;HttpRequestMessage&lt;/code&gt; (POST), adds &lt;code&gt;Authorization: Bearer &amp;lt;APIKEY&amp;gt;&lt;/code&gt;, sends JSON to Reka.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parse Response&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Deserializes into a &lt;code&gt;RekaResponse&lt;/code&gt; domain object, returns the first answer string.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Adding It to VS Code (MCP Extension)
&lt;/h2&gt;

&lt;p&gt;You can run it as a Docker-based MCP server. Two simple approaches:&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Via “Add MCP Server” UI
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;In VS Code (with MCP extension), click &lt;strong&gt;Add MCP Server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose type: &lt;strong&gt;Docker image&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Image name: &lt;code&gt;fboucher/check-in-doc-mcp&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Enter allowed domains and your Reka API key when prompted.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Option 2: Via &lt;code&gt;mcp.json&lt;/code&gt; (Recommended)
&lt;/h3&gt;

&lt;p&gt;Alternatively, you can manually configure it in your &lt;code&gt;mcp.json&lt;/code&gt; file. This will make sure your API key isn't displayed in plain text. Add or merge this configuration:&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&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;"check-in-docs"&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;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"run"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-i"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"--rm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"ALLOWED_DOMAINS=${input:allowed_domains}
        "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"-e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"APIKEY=${input:apikey}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"fboucher/check-in-doc-mcp"&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="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;"inputs"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"allowed_domains"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"promptString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enter the comma-separated list of documentation domains to allow (e.g. docs.reka.ai,docs.github.com):"&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"apikey"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"promptString"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enter your Reka Platform API key:"&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="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;h2&gt;
  
  
  How to Use It
&lt;/h2&gt;

&lt;p&gt;To use it ask to &lt;code&gt;Check In Doc&lt;/code&gt; something or You can now use the &lt;code&gt;SearchInDoc&lt;/code&gt; tool in your MCP-enabled environment. Just ask a question, and it will search only the specified documentation domains.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;It’s intentionally simple—no giant orchestration layer. Just a clean bridge between a question, curated domains, and a research-enabled model. Sometimes that’s all you need to get focused, trustworthy answers.&lt;/p&gt;

&lt;p&gt;If this sparks an idea, clone it and adapt away. If you improve it (citations, richer error handling, multi-turn context)—send a PR!&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch a quick demo
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/sDlm0ru6zxA"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Links &amp;amp; References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/fboucher/check-in-doc-mcp" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.reka.ai/research" rel="noopener noreferrer"&gt;Reka Research API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/reka-ai/reka-research-mcp" rel="noopener noreferrer"&gt;Reka Research MCP Server (in TypeScript)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>vscode</category>
      <category>mcp</category>
    </item>
    <item>
      <title>AI Vision: Turning Your Videos into Comedy Gold (or Cringe)</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Wed, 12 Nov 2025 19:40:00 +0000</pubDate>
      <link>https://dev.to/fboucheros/ai-vision-turning-your-videos-into-comedy-gold-or-cringe-2786</link>
      <guid>https://dev.to/fboucheros/ai-vision-turning-your-videos-into-comedy-gold-or-cringe-2786</guid>
      <description>&lt;p&gt;I've spent most of my career building software in C# and .NET, and only used Python in IoT projects. When I wanted to build a fun project—an app that uses AI to roast videos, I knew it was the perfect opportunity to finally dig into Python web development.&lt;/p&gt;

&lt;p&gt;The question was: where do I start? I hopped into a brainstorming session with &lt;a href="http://app.reka.ai/chat" rel="noopener noreferrer"&gt;Reka's AI chat&lt;/a&gt; and asked about options for building web apps in Python. It mentioned Flask, and I remembered friends talking about it being lightweight and perfect for getting started. That sounded right.&lt;/p&gt;

&lt;p&gt;In this post, I share how I built &lt;strong&gt;"Roast My Life,"&lt;/strong&gt; a Flask app using the Reka Vision API.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vision (Pun Intended)
&lt;/h2&gt;

&lt;p&gt;The app needed three core things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;List videos&lt;/strong&gt; : Show me what videos are in my collection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload videos&lt;/strong&gt; : Let me add new ones via URL&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roast a video&lt;/strong&gt; : Send a selected video to an AI and get back some hilarious commentary&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  See it in action
&lt;/h2&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/INJgi2YHYuc"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 1: Getting Started Environment Setup
&lt;/h2&gt;

&lt;p&gt;The first hurdle was always going to be environment setup. I'm serious about keeping my Python projects isolated, so I did the standard dance:&lt;/p&gt;

&lt;p&gt;Before even touching dependencies, I scaffolded a super bare-bones Flask app. Then one thing I enjoy from C# is that all dependencies are brought in one shot, so I like doing the same with my python projects using &lt;code&gt;requirements.txt&lt;/code&gt; instead of installing things ad‑hoc (&lt;code&gt;pip install flask&lt;/code&gt; then later freezing).&lt;/p&gt;

&lt;p&gt;Dropping that file in first means the setup snippet below is deterministic. When you run &lt;code&gt;pip install -r requirements.txt&lt;/code&gt;, Flask spins up using the exact versions I tested with, and you won't accidentally grab a breaking major update.&lt;/p&gt;

&lt;p&gt;Here's the shell dance that activates the virtual environment and installs everything:&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="nb"&gt;cd &lt;/span&gt;roast_my_life/workshop
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

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

&lt;/div&gt;



&lt;p&gt;Then came the configuration. We will need an API key and I don't want to have it hardoced so I created a &lt;code&gt;.env&lt;/code&gt; file to store my API credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API_KEY=YOUR_REKA_API_KEY
BASE_URL=https://vision-agent.api.reka.ai

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

&lt;/div&gt;



&lt;p&gt;To get that API key, I visited the &lt;a href="https://link.reka.ai/free" rel="noopener noreferrer"&gt;Reka Platform&lt;/a&gt; and grabbed a free one. Seriously, a free key for playing with AI vision APIs? I was in.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;python app.py&lt;/code&gt;, I fired up the Flask development server and opened &lt;code&gt;http://127.0.0.1:5000&lt;/code&gt; in my browser. The UI was there, but... it was dead. Nothing worked.&lt;/p&gt;

&lt;p&gt;Perfect. Time to build.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Backend: Flask Routing and API Integration
&lt;/h2&gt;

&lt;p&gt;Coming from ASP.NET Core's controller-based routing and Blazor, Flask's decorator-based approach felt just like home. All the code code goes in the &lt;code&gt;app.py&lt;/code&gt; file, and each route is defined with a simple decorator. But first things first: loading configuration from the &lt;code&gt;.env&lt;/code&gt; file using &lt;code&gt;python-dotenv&lt;/code&gt;:&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Load environment variables (like appsettings.json)
&lt;/span&gt;&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;BASE_URL&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;All the imports packages are the same ones that needs to be in the &lt;code&gt;requirements.txt&lt;/code&gt;. And we retreive the API key and base URL from environment variables, just like in .NET Core.&lt;/p&gt;

&lt;p&gt;Now, to be able to get roasted we need first to upload a video to the Reka Vision API. Here's the code—I'll go over some details after.&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="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/api/upload_video&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&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;POST&lt;/span&gt;&lt;span class="sh"&gt;'&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;upload_video&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Upload a video to Reka Vision API&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;video_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;video_name&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;video_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;video_url&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="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&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;video_name&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;video_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;Both video_name and video_url are required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;400&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;api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;API key not configured&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/videos/upload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&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;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&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;video_name&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;video_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;index&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;true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# Required: tells Reka to process the video
&lt;/span&gt;                &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;video_url&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;video_url&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;video_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;video_id&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;unknown&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;video_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Video uploaded successfully&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;error_msg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;error_msg&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;Request timed out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;504&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Upload failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Once the information from the frontend is validated we make a POST request to the Reka Vision API's &lt;code&gt;/videos/upload&lt;/code&gt; endpoint. The parameters are sent as form data, and we include the API key in the headers for authentication. Here I was using URLs to upload videos, but you can also upload local files by adjusting the request accordingly. As you can see, it's pretty straightforward, and the &lt;a href="http://docs.reka.ai/vision" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; from Reka made it easy to understand what was needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Magic: Sending Roast Requests to Reka Vision API
&lt;/h2&gt;

&lt;p&gt;Here's where things get interesting. Once a video is uploaded, we can ask the AI to analyze it and generate content. The Reka Vision API supports conversational queries about video content:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;call_reka_vision_qa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Call the Reka Video QA API to generate a roast&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&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;X-Api-Key&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&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;video_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;video_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;messages&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&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;role&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;user&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;content&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;Write a funny and gentle roast about the person, or the voice in this video. Reply in markdown format.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;/qa/chat&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&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;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ok&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;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;HTTP &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; calling chat endpoint&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;requests&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&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;Request to chat API timed out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;Exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Chat API call failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="si"&gt;}&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;Here we pass the video ID and a prompt asking for a "funny and gentle roast." The API responds with AI-generated content, which we can then send back to the frontend for display. I try to give more "freedom" to the AI by asking it to reply in markdown format, which makes the output more engaging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It Yourself!
&lt;/h2&gt;

&lt;p&gt;The complete project is available on GitHub: &lt;strong&gt;&lt;a href="https://github.com/reka-ai/api-examples-python/tree/main/roast_my_life" rel="noopener noreferrer"&gt;reka-ai/api-examples-python&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes Reka Vision API So nice to use
&lt;/h2&gt;

&lt;p&gt;What really stood out to me was how approachable the Reka Vision API is. You don't need any special SDK—just the &lt;code&gt;requests&lt;/code&gt; library making standard HTTP calls. And honestly, it doesn't matter what language you're used to; an HTTP call is pretty much always simple to do. Whether you're coming from .NET, Python, JavaScript, or anything else, you're just sending JSON and getting JSON back.&lt;/p&gt;

&lt;p&gt;Authentication is refreshingly straightforward: just pop your API key in the header and you're good to go. No complex SDKs, no multi-step authentication flows, no wrestling with binary data streams. The conversational interface lets you ask questions in natural language, and you get back structured JSON responses with clear fields.&lt;/p&gt;

&lt;p&gt;One thing worth noting: in this example, the videos are pre-uploaded and indexed, which means the responses come back fast. But here's the impressive part—the AI actually &lt;em&gt;looks&lt;/em&gt; at the video content. It's not just reading a transcript or metadata; it's genuinely analyzing the visual elements. That's what makes the roasts so spot-on and contextual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The Reka Vision API itself deserves credit for making video AI accessible. No complicated SDKs, no multi-GB model downloads, no GPU requirements. Just simple HTTP requests and powerful AI capabilities. I'm not saying I'm switching to Python full-time, but expect to see me sharing more Python projects in the future!&lt;/p&gt;

&lt;h3&gt;
  
  
  References and Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt; : &lt;a href="https://github.com/reka-ai/api-examples-python" rel="noopener noreferrer"&gt;reka-ai/api-examples-python&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join &lt;a href="https://link.reka.ai/discord" rel="noopener noreferrer"&gt;Reka community&lt;/a&gt; and share what you build! You can find me there as &lt;strong&gt;fboucheros&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>demo</category>
      <category>github</category>
    </item>
    <item>
      <title>How to Leverage Reka Research to Build Smarter AI Apps</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Tue, 16 Sep 2025 11:22:04 +0000</pubDate>
      <link>https://dev.to/reka/how-to-leverage-reka-research-to-build-smarter-ai-apps-2l89</link>
      <guid>https://dev.to/reka/how-to-leverage-reka-research-to-build-smarter-ai-apps-2l89</guid>
      <description>&lt;p&gt;Imagine having an assistant that could search across a curated list of sources, gather information, synthesize it, and present it in a structured format tailored to your needs. This is the power of Reka Research, an AI model designed to perform deep research by leveraging web searches and document analysis much faster than a human could. With tools like Reka Research integrated into your applications, you can create smarter, more reliable AI experiences. This post walks you through a demo app that showcases how to use Reka Research and fine-tune it by using advanced options and get better results, faster.&lt;/p&gt;

&lt;p&gt;The following video show step‑by‑step how Reka Research powers a simple “Reka Restaurants” app that turns a craving (e.g., bagels”) into a short, structured list of nearby restaurants—plus a transparent reasoning trace. You can follow along and build it yourself! Clone the repo, and run the app locally.&lt;/p&gt;

&lt;p&gt;Repo: the example lives in &lt;code&gt;reka-restaurants&lt;/code&gt; inside:&lt;br&gt;
&lt;a href="https://github.com/reka-ai/api-examples-typescript" rel="noopener noreferrer"&gt;github.com/reka-ai/api-examples-typescript&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Watch the video
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/UIVi7JvVmY8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Get the code and set up
&lt;/h2&gt;

&lt;p&gt;Prereqs: Node.js 18+ and npm.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repo and install deps
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;reka-restaurants
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Get your Reka API Key&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Visit the &lt;a href="https://platform.reka.ai" rel="noopener noreferrer"&gt;Reka Platform&lt;/a&gt; dashboard&lt;/li&gt;
&lt;li&gt;Open “API keys” in the left nav&lt;/li&gt;
&lt;li&gt;Create a new key (e.g., “reka-restaurants”) and copy it&lt;/li&gt;
&lt;/ul&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%2Fnlzdk8cgcnoa8i6m3a3t.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%2Fnlzdk8cgcnoa8i6m3a3t.png" alt="Get your Reka API Key"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configure your environment
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;REKA_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;your_reka_api_key&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Run the app
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:5173&lt;/code&gt; and try a query like "sushi".&lt;/p&gt;

&lt;h2&gt;
  
  
  The OpenAI‑compatible client
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;src/server.ts&lt;/code&gt;, the constant &lt;code&gt;reka_research&lt;/code&gt; is the OpenAI‑compatible client for Reka:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reka_research&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REKA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baseURL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.reka.ai/v1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it trivial to swap in Reka Research without changing your app stack.&lt;br&gt;
You only need to change the model to 'reka-flash-research' and the baseURL.&lt;/p&gt;
&lt;h2&gt;
  
  
  Structured outputs
&lt;/h2&gt;

&lt;p&gt;A great feature of Reka Research is the ability to specify the response format. This is extremely useful when you want to use the output directly in your application without additional parsing or processing. In our current example, by defining the expected answer to be a list of restaurants with the following information: name, cuisine type, address, price range, rating, url, why it was selected, that is exactly what we get back. This list can then be used directly by our application, saved to a database, or displayed to users, without any additional parsing or processing. Even better, specifying the schema helps guide the model to produce more accurate and relevant results.&lt;/p&gt;

&lt;p&gt;The Reka Restaurant backend defines a response schema &lt;code&gt;RestaurantItemSchema&lt;/code&gt; that the model must follow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RestaurantItemSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;cuisine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;neighborhood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;approx_price&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$$$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$$$$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;rating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;distance_km&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;number&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;why&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;nullable&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you submit a query, the agent performs web searches to gather relevant information. This may result in searching more websites to make sure all relevant information is found. The agent then opens and analyzes the content of these pages to extract the required details about each restaurant. Finally, it compiles this information into a structured list that adheres to the defined schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grounding results with the user’s location
&lt;/h2&gt;

&lt;p&gt;Instead of requiring users to type “near me” or specify a city, the app automatically uses the browser’s location to populate the API’s location parameter. This enables more accurate, relevant restaurant recommendations and a smoother user experience.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/recommendations&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApproxLocation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;timezone&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tuning breadth vs. latency with &lt;code&gt;max_uses&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Reka Research performs a series of steps (search, open, analyze). For simpler queries or when latency matters limit the number of web searches the agent performs:&lt;/p&gt;

&lt;p&gt;Change the &lt;code&gt;max_uses&lt;/code&gt; to the level of your choice. In this case, set it to &lt;code&gt;1&lt;/code&gt; to allow only one web search before returning results. The agent may still open and analyze pages, but the trajectory is shorter and returns faster. This provides a good balance between depth and speed depending on the use case—sometimes you want a quick answer, other times you want more thorough research.&lt;/p&gt;

&lt;p&gt;Try raising or lowering this value depending on how broad/deep you want the research to go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;research&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;web_search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Can be used to restrict the search to a specific domain(s). For example, to only search TripAdvisor.&lt;/span&gt;
    &lt;span class="c1"&gt;// A separate related field is "blocked_domains" to block specific domains.&lt;/span&gt;
    &lt;span class="na"&gt;allowed_domains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tripadvisor.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="c1"&gt;// Limit the number of web searches Reka Research can do.&lt;/span&gt;
    &lt;span class="na"&gt;max_uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// Can specify the user's location to ground the agent's search / response.&lt;/span&gt;
    &lt;span class="na"&gt;user_location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;approximate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;country&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;country&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;city&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;city&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Controlling sources with &lt;code&gt;allowed_domains&lt;/code&gt; and &lt;code&gt;blocked_domains&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;When doing research, you may want to control which sources the agent can use. Online, not all sources are equally reliable or relevant for your use case. There are two parameters to help fine-tune the desired sources: &lt;code&gt;allowed_domains&lt;/code&gt; and &lt;code&gt;blocked_domains&lt;/code&gt;. In the code above, we set &lt;code&gt;allowed_domains&lt;/code&gt; to only search "TripAdvisor". This limits the research to only that specific domain. This is useful when you want to ensure that the information comes from a curated list of sources. You can also use &lt;code&gt;blocked_domains&lt;/code&gt; to exclude specific domains that you know are not reliable or relevant for your needs. These options give you a simple trust policy you can tune per use case.&lt;/p&gt;

&lt;p&gt;Try adding or removing domains in &lt;code&gt;allowed_domains&lt;/code&gt; or experiment with &lt;code&gt;blocked_domains&lt;/code&gt; to see how it impacts the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transparent reasoning
&lt;/h2&gt;

&lt;p&gt;The response includes a reasoning trace (the agent’s searches and analyses). This is very helpful to validate the information returned or understand why some results were or weren't selected in the final response. In this demo application, the frontend renders these steps so users can see which sources were queried and how results were formed.&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%2Fr7gfl9kmszjgsn25h4kx.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%2Fr7gfl9kmszjgsn25h4kx.png" alt="reasoning trace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Reka Research provides a practical foundation for building more powerful and intelligent applications. By adding the ability to actively search, analyze multiple sources, and return structured data, you can create apps that go beyond simple text generation. The transparency of the research process—showing which sources were used and how conclusions were reached—adds a layer of reliability that users can trust. It's this combination of enhanced capabilities and verifiable results that makes applications truly smarter.&lt;/p&gt;

&lt;p&gt;What makes Reka Research particularly compelling is how effortlessly it integrates into existing workflows. If you're already using OpenAI's API, switching to Reka Research is literally a two-line change: update your baseURL to &lt;code&gt;https://api.reka.ai/v1&lt;/code&gt; and change the model to &lt;code&gt;reka-flash-research&lt;/code&gt;. From there, you unlock powerful capabilities like location-grounded search, source control, and structured outputs that just work.&lt;/p&gt;

&lt;p&gt;Whether you're building a restaurant finder, a research assistant, or any application that needs verified information, Reka Research gives you the tools to create smarter, more reliable AI experiences. Ready to try it yourself? Clone the &lt;a href="https://github.com/reka-ai/api-examples-typescript" rel="noopener noreferrer"&gt;restaurant finder repo&lt;/a&gt; and see the difference that deep research can make, or simply swap the URL in your existing OpenAI application and start experimenting today.&lt;/p&gt;

&lt;p&gt;Have feedback or want to show what you built? Join our &lt;a href="https://link.reka.ai/discord" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt; and check the docs at &lt;a href="https://docs.reka.ai" rel="noopener noreferrer"&gt;docs.reka.ai&lt;/a&gt;. We’d love to see what you create.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>openai</category>
      <category>api</category>
    </item>
    <item>
      <title>Using AI with .NET 10 Scripts: What Worked, What Didn’t, and Lessons Learned</title>
      <dc:creator>Frank Boucher ☁</dc:creator>
      <pubDate>Thu, 04 Sep 2025 14:57:00 +0000</pubDate>
      <link>https://dev.to/fboucheros/using-ai-with-net-10-scripts-what-worked-what-didnt-and-lessons-learned-4km3</link>
      <guid>https://dev.to/fboucheros/using-ai-with-net-10-scripts-what-worked-what-didnt-and-lessons-learned-4km3</guid>
      <description>&lt;p&gt;I wanted to kick the tires on the upcoming .NET 10 C# script experience and see how far I could get calling Reka’s Research LLM from a single file, no project scaffolding, no .csproj. This isn’t a benchmark; it’s a practical tour to compare ergonomics, setup, and the little gotchas you hit along the way. I’ll share what worked, what didn’t, and a few notes you might find useful if you try the same.&lt;/p&gt;

&lt;p&gt;All the sample code (and a bit more) is here: &lt;a href="https://github.com/reka-ai/api-examples-dotnet/tree/main/csharp10-script" rel="noopener noreferrer"&gt;reka-ai/api-examples-dotnet · csharp10-script&lt;/a&gt;. The scripts run a small “top 3 restaurants” prompt so you can validate everything quickly.&lt;/p&gt;

&lt;p&gt;We’ll make the same request in three ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OpenAI SDK&lt;/li&gt;
&lt;li&gt;Microsoft.Extensions.AI for OpenAI&lt;/li&gt;
&lt;li&gt;Raw HttpClient&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;p&gt;The C# "script" feature used below ships with the upcoming .NET 10 and is &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/10.0" rel="noopener noreferrer"&gt;currently available in preview&lt;/a&gt;. If you prefer not to install a preview SDK, you can run everything inside the provided Dev Container or on GitHub Codespaces. I include a &lt;code&gt;.devcontainer&lt;/code&gt; folder with everything set up in the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up your API key
&lt;/h2&gt;

&lt;p&gt;We are talking about APIs here so of course, you need an API key. Good news is that it's free to sign up with &lt;a href="https://platform.reka.ai" rel="noopener noreferrer"&gt;Reka&lt;/a&gt; and get one! It's a two-click process; more details are in the repository. The API key is then stored in a &lt;code&gt;.env&lt;/code&gt; file, and each script loads environment variables using &lt;code&gt;DotNetEnv.Env.Load()&lt;/code&gt;, so your key is picked up automatically. I went this way instead of using &lt;code&gt;dotnet user-secrets&lt;/code&gt; because I thought it would be the way it would be done in a CI/CD pipeline or a quick script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the demos
&lt;/h2&gt;

&lt;p&gt;From the &lt;code&gt;csharp10-script&lt;/code&gt; folder, run any of these scripts. Each line is an alternative.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet run 1-try-reka-openai.cs
dotnet run 2-try-reka-ms-ext.cs
dotnet run 3-try-reka-http.cs

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

&lt;/div&gt;



&lt;p&gt;You should see a short list of restaurant suggestions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://blogger.googleusercontent.com/img/a/AVvXsEia7e6Tdjv6W0kL5MMINWhk6REYzi4uC-7D4cAbiTgQ3-nESNp_CWHgkZy-oPCCgWNeJd4iiVcDTGzlwjEJLviMopGPCeADN7VXuPoySV3HtrPO8sO9Ci59jQZRisuZi7baZkpOQA_bJpNAoq3t7awddvc6h5w4NVs4N0kWVgigLmAsNjXDfH6Il9nz89k" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fblogger.googleusercontent.com%2Fimg%2Fa%2FAVvXsEia7e6Tdjv6W0kL5MMINWhk6REYzi4uC-7D4cAbiTgQ3-nESNp_CWHgkZy-oPCCgWNeJd4iiVcDTGzlwjEJLviMopGPCeADN7VXuPoySV3HtrPO8sO9Ci59jQZRisuZi7baZkpOQA_bJpNAoq3t7awddvc6h5w4NVs4N0kWVgigLmAsNjXDfH6Il9nz89k%3Dw640-h341" alt="Prompt Result: 3 restaurants"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAI SDK with a custom endpoint
&lt;/h2&gt;

&lt;p&gt;Reka's APIs are using the OpenAI format; therefore, I thought of using the NuGet package &lt;a href="https://www.nuget.org/packages/OpenAI" rel="noopener noreferrer"&gt;OpenAI&lt;/a&gt;. To reference a package in a script, you use the &lt;code&gt;#:package [package name]@[package version]&lt;/code&gt; directive at the top of the file. Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="m"&gt;2.3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;

&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://api.reka.ai/v1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;openAiClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpenAIClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ApiKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REKA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenAIClientOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openAiClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reka-flash-research"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Give me 3 nice, not crazy expensive, restaurants for a romantic dinner in Montreal"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;CompleteChatAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ChatMessage&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UserChatMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;generatedText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;completion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$" Result: \n&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;generatedText&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&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 rest of the code is more straightforward, you create a chat client, specify the Reka API URL, select the model, and then you send a prompt. And it works just as expected. However, not everything was perfect, but before I share more about that part, let's talk about &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft Extensions AI for OpenAI
&lt;/h2&gt;

&lt;p&gt;Another common way to use LLM in .NET is to use one ot the &lt;code&gt;Microsoft.Extensions.AI&lt;/code&gt; NuGet packages. In our case &lt;a href="https://www.nuget.org/packages/Microsoft.Extensions.AI.OpenAI/" rel="noopener noreferrer"&gt;Microsoft.Extensions.AI.OpenAI&lt;/a&gt; was used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;package&lt;/span&gt; &lt;span class="n"&gt;Microsoft&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OpenAI&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="m"&gt;9.8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;-&lt;/span&gt;&lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;1.25412&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;

&lt;span class="c1"&gt;// ....&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://api.reka.ai/v1"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;IChatClient&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ChatClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"reka-flash-research"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ApiKeyCredential&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;REKA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OpenAIClientOptions&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;AsIChatClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Give me 3 nice, not crazy expensive, restaurants for a romantic dinner in Montreal"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetResponseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;As you can see, the code is very similar. Create a chat client, set the URL, the model, and add your prompt, and it works just as well.&lt;/p&gt;

&lt;p&gt;That's two ways to use Reka API with different SDKs, but maybe you would prefer to go "SDKless", let's see how to do that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Raw HttpClient calling the REST API
&lt;/h2&gt;

&lt;p&gt;Without any SDK to help, there is a bit more line of code to write, but it's still pretty straightforward. Let's see the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"http://api.reka.ai/v1/chat/completions"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;requestPayload&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"reka-flash-research"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;new&lt;/span&gt;
                &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;role&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Give me 3 nice, not crazy expensive, restaurants for a romantic dinner in New York city"&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;HttpRequestMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HttpMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;$"Bearer &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;REKA_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;StringContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonPayload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Encoding&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTF8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;httpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SendAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;responseContent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ReadAsStringAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;jsonDocument&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;responseContent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;contentString&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDocument&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RootElement&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"choices"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contentString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;So you create an &lt;code&gt;HttpClient&lt;/code&gt;, prepare a request with the right headers and payload, send it, get the response, and parse the JSON to extract the text. In this case, you have to know the JSON structure of the response, but it follows the OpenAI format.&lt;/p&gt;

&lt;h2&gt;
  
  
  What did I learn from this experiment?
&lt;/h2&gt;

&lt;p&gt;I used VS Code while trying the script functionality. One thing that surprised me was that I didn't get any IntelliSense or autocompletion. I tried to disable the DevKit extension and change the setting for OmniSharp but no luck. My guess is that because it's in preview, and it will works just fine in November 2025 when .NET 10 will be released.&lt;/p&gt;

&lt;p&gt;In this light environment, I encountered some issues where, for some reason, I couldn't use a &lt;code&gt;https&lt;/code&gt; endpoint, I had to use &lt;code&gt;http&lt;/code&gt;. In the raw httpClient script, I had some errors with the Reflection that wasn't available. It could be related to the preview or something else, I didn't investigate further.&lt;/p&gt;

&lt;p&gt;For the most part, everything worked as expected. You can use C# code to quickly execute some tasks without any project scaffolding. It's a great way to try out the Reka API and see how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;While writing those scripts, I encountered multiple issues that aren't related to .NET but more about the SDKs when trying to do more advanced functionalities like optimization of the query and formatting the response output. Since it goes beyond the scope of this post, I will share my findings in a follow-up post. Stay tuned!&lt;/p&gt;

&lt;h3&gt;
  
  
  Video version
&lt;/h3&gt;

&lt;p&gt;Here is a video version of this post&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/JwFHKQkah30"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reka docs: &lt;a href="https://docs.reka.ai/" rel="noopener noreferrer"&gt;docs.reka.ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Join the community on Discord: &lt;a href="https://discord.com/invite/MTRJEBvH" rel="noopener noreferrer"&gt;discord invite&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Github: &lt;a href="https://github.com/reka-ai/api-examples-dotnet" rel="noopener noreferrer"&gt;API Example for .NET&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>dotnet</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
