<?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: Daniel Chege</title>
    <description>The latest articles on DEV Community by Daniel Chege (@blews).</description>
    <link>https://dev.to/blews</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2390863%2F64297842-3bd8-4d39-b386-2e299cab5ed3.png</url>
      <title>DEV Community: Daniel Chege</title>
      <link>https://dev.to/blews</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/blews"/>
    <language>en</language>
    <item>
      <title>Asking my CI "why did that fail?": GitHub Actions cognee</title>
      <dc:creator>Daniel Chege</dc:creator>
      <pubDate>Wed, 28 Oct 2026 13:03:46 +0000</pubDate>
      <link>https://dev.to/blews/asking-my-ci-why-did-that-fail-github-actions-cognee-2b9a</link>
      <guid>https://dev.to/blews/asking-my-ci-why-did-that-fail-github-actions-cognee-2b9a</guid>
      <description>&lt;p&gt;I wanted to ask my CI a plain question: "which workflows failed recently, and why?" So I built a connector that turns GitHub Actions runs into memory an LLM can answer from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: CI history is hard to ask questions of
&lt;/h2&gt;

&lt;p&gt;When a build goes red, finding out why means a lot of clicking. You open the Actions tab, pick a run, expand the failed job, then scroll a raw log full of timestamps and ANSI codes until you find the &lt;code&gt;##[error]&lt;/code&gt; line.&lt;/p&gt;

&lt;p&gt;That works for one run. It breaks down when you want the pattern: is this the same test that failed last week? Which workflow keeps needing approval? What changed on &lt;code&gt;main&lt;/code&gt; lately? The answers are spread over dozens of runs, and none of them are searchable together.&lt;/p&gt;

&lt;p&gt;I ran into this myself while contributing to cognee-community from a fork. In its Actions tab, my runs sat alongside many others marked &lt;code&gt;action_required&lt;/code&gt;, because workflows on fork PRs wait for a maintainer to approve them. Real failures were mixed in with those waiting runs, and the only way to tell them apart was to open each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;I contributed a &lt;strong&gt;GitHub Actions data-source connector&lt;/strong&gt; to &lt;a href="https://github.com/topoteretes/cognee-community" rel="noopener noreferrer"&gt;cognee-community&lt;/a&gt; (&lt;a href="https://github.com/topoteretes/cognee-community/pull/383" rel="noopener noreferrer"&gt;PR #383&lt;/a&gt;). &lt;a href="https://github.com/topoteretes/cognee" rel="noopener noreferrer"&gt;cognee&lt;/a&gt; is an open-source memory layer for AI apps: you add data, it builds a knowledge graph plus embeddings, and you query it in natural language.&lt;/p&gt;

&lt;p&gt;The connector is a single function, &lt;code&gt;github_actions_source()&lt;/code&gt;. You give it one or more &lt;code&gt;owner/repo&lt;/code&gt; names, and it returns a &lt;a href="https://dlthub.com" rel="noopener noreferrer"&gt;dlt&lt;/a&gt; source that cognee can ingest directly. Each completed workflow run becomes one readable document. Failed runs can include a short summary of their log instead of the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzsagkpfl9wtmrvs2tdf.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flzsagkpfl9wtmrvs2tdf.png" alt=" " width="800" height="238"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The connector does the tedious part: it reads the API, trims the logs and writes each run up as markdown. cognee does the rest.&lt;/p&gt;

&lt;p&gt;Here is the real document for the failed bump run from the demo below. Its whole failure log comes down to two error lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Repository: topoteretes/cognee-community
Workflow: automation | Bump cognee in the database adapters (.github/workflows/bump_cognee.yml)
Run: #22, attempt 1
Conclusion: failure
Event: schedule
Branch: main
Commit: 5a595a6 Merge pull request #349 from AbdulWasih05/feat/asana-connector
Started: 2026-10-28T06:33:36Z
Finished: 2026-10-28T06:33:45Z

&lt;span class="gu"&gt;## Jobs&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; bump: failure
&lt;span class="p"&gt;  -&lt;/span&gt; failed step: Check the PR token is configured

&lt;span class="gu"&gt;## Failure log: bump&lt;/span&gt;
Errors:
&lt;span class="p"&gt;-&lt;/span&gt; Set the COGNEE_BUMP_PR_TOKEN secret (see the header of this workflow).
&lt;span class="p"&gt;-&lt;/span&gt; Process completed with exit code 1.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few kilobytes of noisy log become a dozen lines an LLM can reason over. Each document also keeps the run's URL, so an answer can always be traced back to GitHub.&lt;/p&gt;

&lt;h2&gt;
  
  
  The demo: 15 real runs, three questions
&lt;/h2&gt;

&lt;p&gt;I pointed the connector at the 15 latest completed runs of &lt;code&gt;topoteretes/cognee-community&lt;/code&gt; on 28 October 2026. The LLM was an Azure OpenAI deployment, and embeddings came from &lt;code&gt;text-embedding-3-large&lt;/code&gt;. Everything below is real output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Fetch the runs.&lt;/strong&gt; The connector pulled 15 runs, their jobs, and the logs of the failed jobs in &lt;strong&gt;8.2 s&lt;/strong&gt;. Only 3 had actually failed. 10 were waiting for approval.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fniepq1q1dajqhha0q6u2.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fniepq1q1dajqhha0q6u2.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Build the memory.&lt;/strong&gt; One call, &lt;code&gt;cognee.remember()&lt;/code&gt;, stored the 15 documents and built the knowledge graph and embeddings in &lt;strong&gt;105.3 s&lt;/strong&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;github_actions_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;repos&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;topoteretes/cognee-community&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;max_runs_per_repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;include_failed_logs&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="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;dataset_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github-actions-blog&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Ask questions.&lt;/strong&gt; Here are cognee's answers, unedited:&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fems3wu2qofqj1ws1wdad.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fems3wu2qofqj1ws1wdad.png" alt=" " width="800" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Which workflows failed recently, and why?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On 2026-10-28, three workflows failed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clay connector tests (#1):&lt;/strong&gt; Python 3.11 and 3.12 both failed &lt;code&gt;test_check_active_cancellation&lt;/code&gt;; extraction raised &lt;code&gt;ResourceExtractionError: Sync cancelled by host&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruff Lint and Format (#1098):&lt;/strong&gt; Clay code had a nested-if lint issue, unescaped regex patterns in tests, and a formatting issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduled "Bump cognee in the database adapters" (#22):&lt;/strong&gt; failed because the &lt;code&gt;COGNEE_BUMP_PR_TOKEN&lt;/code&gt; secret was not configured.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Klaviyo connector runs were marked &lt;code&gt;action_required&lt;/code&gt;, not failed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I fix the failing bump workflow?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Add the &lt;code&gt;COGNEE_BUMP_PR_TOKEN&lt;/code&gt; secret to the repository's GitHub Actions secrets (see the workflow header for details), then rerun the scheduled workflow. It failed because that required secret wasn't configured.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Q: Which workflows are waiting for approval?&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The workflows marked &lt;code&gt;action_required&lt;/code&gt; (waiting for approval) are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Klaviyo connector tests&lt;/li&gt;
&lt;li&gt;Vercel connector tests&lt;/li&gt;
&lt;li&gt;Prometheus connector&lt;/li&gt;
&lt;li&gt;Ruff Lint and Format (runs #1094, #1095, #1101, and #1102)&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first two answers are what I wanted. Each gives the cause and the fix, down to the failing test name and the missing secret, with no log open. The first even points out that the Klaviyo runs were waiting, not failing.&lt;/p&gt;

&lt;p&gt;The third answer is close but not complete. It lists 7 of the 10 waiting runs and misses Ruff runs #1096, #1097 and #1100. Graph completion answers from the context it retrieves, so it isn't an exhaustive query. For an exact list, the table from step 1 is still the right tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it can help your workflow
&lt;/h2&gt;

&lt;p&gt;It helps most when you need the story across many runs, not the details of one.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Who&lt;/th&gt;
&lt;th&gt;Question they can ask&lt;/th&gt;
&lt;th&gt;What used to happen&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maintainer&lt;/td&gt;
&lt;td&gt;"Which PRs are stuck waiting for workflow approval?"&lt;/td&gt;
&lt;td&gt;Scan the Actions tab run by run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contributor&lt;/td&gt;
&lt;td&gt;"Why did the tests fail on my branch?"&lt;/td&gt;
&lt;td&gt;Open the job, expand the step, scroll the raw log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;On-call / release owner&lt;/td&gt;
&lt;td&gt;"Is this the same error that broke &lt;code&gt;main&lt;/code&gt; last week?"&lt;/td&gt;
&lt;td&gt;Rely on memory, or search Slack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team lead&lt;/td&gt;
&lt;td&gt;"Summarize recent CI activity in this repo"&lt;/td&gt;
&lt;td&gt;Build a dashboard, or just not know&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI coding agent&lt;/td&gt;
&lt;td&gt;Reads the failure summary before proposing a fix&lt;/td&gt;
&lt;td&gt;Fed a whole log, or nothing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because it's a plain cognee dataset, CI history can sit next to your other data, such as docs, issues or code. One question can then draw on all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself
&lt;/h2&gt;

&lt;p&gt;You need Python 3.11 to 3.13, a GitHub token, and an LLM key for cognee. For a public repo, a token with no extra scopes is enough. For a private repo it needs &lt;code&gt;repo&lt;/code&gt; or &lt;code&gt;actions:read&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;git clone https://github.com/topoteretes/cognee-community
&lt;span class="nb"&gt;cd &lt;/span&gt;cognee-community/packages/connector/github-actions
uv &lt;span class="nb"&gt;sync
export &lt;/span&gt;&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gh auth token&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LLM_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...   &lt;span class="c"&gt;# or put cognee's settings in a .env file&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cognee_community_connector_github_actions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;github_actions_source&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;github_actions_source&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;repos&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;owner/repo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;max_runs_per_repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;include_failed_logs&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="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remember&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataset_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;github-actions&lt;/span&gt;&lt;span class="sh"&gt;"&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;query_text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which workflows failed recently, and why?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;query_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cognee&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GRAPH_COMPLETION&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;datasets&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;github-actions&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="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;workflow&lt;/code&gt; and &lt;code&gt;branch&lt;/code&gt; narrow the runs to one workflow file or one branch.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;created_after&lt;/code&gt; limits how far back to go.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;log_tail_lines&lt;/code&gt; controls how much of each failed step's output is kept (default 50).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it again later to sync. Runs that have dropped out of the window are removed from memory, so the dataset tracks your current CI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the PR is still open when you read this, use the branch from &lt;a href="https://github.com/topoteretes/cognee-community/pull/383" rel="noopener noreferrer"&gt;PR #383&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What I learned building it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The model needs the error, not the log.&lt;/strong&gt; Raw Actions logs are mostly setup noise. Keeping only the &lt;code&gt;##[error]&lt;/code&gt; lines and the output just before them made the documents small and the answers sharper.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The network was the slow part.&lt;/strong&gt; Fetching 20 runs took about 16 to 24 s, almost all of it waiting on the API. Fetching jobs in parallel helped. Asking GitHub for only the tail of a log did not, because the log storage ignores range requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be polite to the API.&lt;/strong&gt; The connector backs off and retries on rate limits and server errors instead of failing the whole sync.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test without the network.&lt;/strong&gt; The 31 tests replay fake GitHub responses, so they run in seconds and need no token.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the whole pipeline.&lt;/strong&gt; I confirmed sync, cognify and search end to end, and that re-syncing a smaller window removes old runs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you try it, I'd like to hear what you ask your CI. Feedback and issues on the PR are welcome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt; &lt;a href="https://github.com/topoteretes/cognee" rel="noopener noreferrer"&gt;cognee&lt;/a&gt; · &lt;a href="https://github.com/topoteretes/cognee-community" rel="noopener noreferrer"&gt;cognee-community&lt;/a&gt; · &lt;a href="https://github.com/topoteretes/cognee-community/pull/383" rel="noopener noreferrer"&gt;PR #383&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>devops</category>
      <category>github</category>
      <category>llm</category>
    </item>
    <item>
      <title>EcoSound: Using Open-Source AI to Get People Outside and Exploring Nature 🌿</title>
      <dc:creator>Daniel Chege</dc:creator>
      <pubDate>Mon, 12 Oct 2026 02:24:05 +0000</pubDate>
      <link>https://dev.to/blews/ecosound-using-open-source-ai-to-get-people-outside-and-exploring-nature-2m0h</link>
      <guid>https://dev.to/blews/ecosound-using-open-source-ai-to-get-people-outside-and-exploring-nature-2m0h</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;What if we used AI to get people off their screens instead of keeping them glued to them?&lt;/p&gt;

&lt;p&gt;That question inspired me to build EcoSound, an open-source, AI-powered application that turns ordinary outdoor experiences into opportunities to discover wildlife.&lt;/p&gt;

&lt;p&gt;We're surrounded by nature, but how often do we actually stop to notice it? You might hear a bird singing outside your window, while walking through a park, or on your way to work, without knowing what it is.&lt;/p&gt;

&lt;p&gt;EcoSound makes those small moments of curiosity more meaningful.&lt;/p&gt;

&lt;p&gt;Instead of endlessly scrolling, users can step outside, listen to their surroundings, identify possible bird species, learn about wildlife, and take part in outdoor challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ecosound-web.onrender.com/" rel="noopener noreferrer"&gt;https://ecosound-web.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Daniel-1600/hactoberfest-week-1" rel="noopener noreferrer"&gt;https://github.com/Daniel-1600/hactoberfest-week-1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;

&lt;p&gt;I wanted open-source AI to be central to EcoSound, rather than simply adding an AI chatbot to an otherwise unrelated application.&lt;/p&gt;

&lt;p&gt;To achieve that, I integrated two complementary AI models.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;BirdNET 3.0 — Understanding wildlife sounds&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;BirdNET is an open-source acoustic machine learning system for bird identification.&lt;/p&gt;

&lt;p&gt;Users record or upload audio. The Go backend processes the recording and forwards it to BirdNET for inference.&lt;/p&gt;

&lt;p&gt;BirdNET returns candidate species, model scores, and the portions of the recording where calls were detected.&lt;/p&gt;

&lt;p&gt;I deployed BirdNET on Modal using CPU inference, avoiding the need to run the model on users' devices.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Qwen3 1.7B — Turning discoveries into learning experiences&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Identifying a bird is useful, but I wanted EcoSound to do more than return a species name.&lt;/p&gt;

&lt;p&gt;I integrated Qwen3 1.7B, an open-weight language model, to provide educational explanations and outdoor activity recommendations.&lt;/p&gt;

&lt;p&gt;Before generation, the backend retrieves curated species information, including habitat, diet, ecological role, conservation references, and ethical observation guidance.&lt;/p&gt;

&lt;p&gt;That information is passed to Qwen so its response can be grounded in the available species record.&lt;/p&gt;

&lt;p&gt;The backend validates the generated JSON and requires exactly three outdoor activities.&lt;/p&gt;

&lt;p&gt;Qwen runs on a Modal GPU, independently of BirdNET.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Building the application&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My technology stack:&lt;/p&gt;

&lt;p&gt;Frontend :Next.js, React, TypeScript&lt;/p&gt;

&lt;p&gt;Backend :Go&lt;/p&gt;

&lt;p&gt;Database :SQLite&lt;/p&gt;

&lt;p&gt;Bird identification : BirdNET 3.0&lt;/p&gt;

&lt;p&gt;Nature explanations : Qwen3 1.7B&lt;/p&gt;

&lt;p&gt;Model hosting : Modal&lt;/p&gt;

&lt;p&gt;Web hosting : Render&lt;/p&gt;

&lt;p&gt;Maps : MapLibre GL JS&lt;/p&gt;

&lt;p&gt;Offline capabilities : PWA, IndexedDB, service worker&lt;/p&gt;

&lt;p&gt;The Go backend connects the user-facing application to both AI services, manages stored discoveries, and handles validation and error responses.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Challenges I encountered&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the biggest challenges was getting reliable inference from the cloud-hosted models.&lt;/p&gt;

&lt;p&gt;Qwen initially returned authentication errors because the Modal proxy credentials were incorrect.&lt;/p&gt;

&lt;p&gt;After resolving authentication, requests still failed during model cold starts. A direct request eventually returned HTTP 200 after approximately 185 seconds, including an HTTP 303 redirect.&lt;/p&gt;

&lt;p&gt;I updated the Go client to handle Modal redirects safely and extended the request timeouts.&lt;/p&gt;

&lt;p&gt;Another issue appeared when Qwen returned an explanation that failed the backend's JSON and content validation.&lt;/p&gt;

&lt;p&gt;I refined the generation instructions, added controlled validation logging and retry handling, and expanded the tests.&lt;/p&gt;

&lt;p&gt;Eventually, the complete pipeline returned HTTP 200, with the predicted species, curated wildlife information, an AI-generated explanation, and three outdoor activities.&lt;/p&gt;

&lt;p&gt;This was an important lesson: deploying a model is only part of building an AI application. Handling cold starts, unreliable responses, validation, and model uncertainty is equally important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;EcoSound would have been much harder to build as an independent project without accessible open-source AI.&lt;/p&gt;

&lt;p&gt;First, open models give developers control.&lt;/p&gt;

&lt;p&gt;I can inspect how BirdNET is integrated, build my own inference service, and choose where to run the model. With Qwen, I can control the prompts, generation settings, and validation behavior.&lt;/p&gt;

&lt;p&gt;Second, open models make experimentation more accessible.&lt;/p&gt;

&lt;p&gt;Instead of being tied entirely to proprietary AI APIs, I can deploy models on infrastructure I choose, compare inference options, and adapt the system as my requirements change.&lt;/p&gt;

&lt;p&gt;Third, open innovation encourages reusable solutions.&lt;/p&gt;

&lt;p&gt;EcoSound combines existing research and open technologies into a new experience focused on biodiversity discovery and outdoor exploration.&lt;/p&gt;

&lt;p&gt;I didn't need to train a bird identification model or a language model from scratch. I could build on the work of open-source communities and focus on solving the user-facing problem.&lt;/p&gt;

&lt;p&gt;That doesn't mean every component is free to run or that every model has identical licensing terms. EcoSound's application code is MIT-licensed, Qwen3 1.7B uses Apache 2.0, and BirdNET's model weights have separate noncommercial licensing restrictions.&lt;/p&gt;

&lt;p&gt;For me, that's what makes open innovation exciting: it allows independent developers to turn existing research into useful experiences that might otherwise be out of reach.&lt;/p&gt;

&lt;p&gt;And sometimes, the best use of AI isn't keeping us online.&lt;/p&gt;

&lt;p&gt;It's helping us appreciate what's happening outside.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Best Use of Render&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I deployed the EcoSound web application using Render, making the project accessible through a public URL without requiring judges to install the application locally.&lt;/p&gt;

&lt;p&gt;Render serves as the public entry point to an application powered by open-source AI models hosted independently on Modal.&lt;/p&gt;

&lt;p&gt;Live application: &lt;a href="https://ecosound-web.onrender.com/" rel="noopener noreferrer"&gt;https://ecosound-web.onrender.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;EcoSound — Go Outside. Listen. Discover. 🌿&lt;/p&gt;

&lt;p&gt;Instead of building another application designed to capture more screen time, I wanted to build something that makes the real world a little more interesting.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
