Skip to content

Add Kimi AI filter plugin - #182

Merged
id774 merged 1 commit into
masterfrom
claude/dreamy-sagan-omgzle
Sep 11, 2026
Merged

Add Kimi AI filter plugin#182
id774 merged 1 commit into
masterfrom
claude/dreamy-sagan-omgzle

Conversation

@id774

@id774 id774 commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Summary

Adds FilterKimi, a new independent AI filter plugin for Moonshot AI's Kimi API (default model kimi-k3), following the exact architecture of the existing service-specific AI filters (FilterOpenAI, FilterClaude, FilterGemini, FilterSakuraAI). Kimi's Chat Completions protocol happens to be OpenAI-compatible, but this is deliberately its own plugin rather than a mode of FilterOpenAI — matching how FilterSakuraAI (also OpenAI-compatible) is already handled.

Behavior

  • New file plugins/filter/kimi.rb, class Automatic::Plugin::FilterKimi.
  • Fixed endpoint constant https://api.moonshot.ai/v1/chat/completions (not a Recipe setting), OPEN_TIMEOUT = 10, READ_TIMEOUT = 300 — mirrors sakura_ai.rb/open_ai.rb exactly.
  • Required settings: token, model, prompt — validated up front, each missing/empty one raises ArgumentError before any request.
  • Request body is exactly {"model", "messages": [{"role": "system", "content": <prompt>}, {"role": "user", "content": <description>}]} — no sampling, reasoning, or tool-use parameters are ever sent.
  • Success requires choices[0].finish_reason == "stop" and non-empty choices[0].message.content; reasoning_content, when Kimi sends one, is never read, logged, or written anywhere.
  • Non-"stop" finish reason, invalid JSON, or a missing/wrong-shaped response is a permanent Error (not retried) and never overwrites the description.
  • Network failures, HTTP 429, and HTTP 5xx are TemporaryError and retried up to the configured retry count with interval seconds between attempts; other failures raise Error immediately. Exhausting retries raises Error and ends the run.
  • An item with a blank description is skipped (logged as a warning), leaving it untouched; feed/item count, order, title, link, and date are never touched — only description changes, and only on success.

Compatibility

  • No framework, Recipe format, or plugin-loader change.
  • No new runtime or development dependency (Gemfile and automatic.gemspec untouched) — only json, net/http, openssl, uri from the standard library, same as the other AI filters.
  • No existing AI filter plugin (open_ai.rb, claude.rb, gemini.rb, sakura_ai.rb) was modified.
  • VERSION remains 26.09; doc/VERSIONS' existing unreleased v26.09 (Release Date: TBD) entry gets one new bullet (no new version section created).

Security

  • The token is read once, sent only as Authorization: Bearer <token>, and never appears in a log line or in any raised exception's message — verified by a dedicated spec.
  • TLS certificate verification stays on (OpenSSL::SSL::VERIFY_PEER), matching every other AI filter.
  • Only placeholder credentials (YOUR_KIMI_API_KEY) appear anywhere in the diff — no real-looking key.

Documentation

  • doc/PLUGINS.md: new FilterKimi — Supported (external) catalogue entry (section 6.3), following the FilterSakuraAI entry's structure; updated the "changing service is changing the one entry" line to include FilterKimi.
  • doc/AI_TUTORIAL.md: added a FilterKimi example alongside FilterOpenAI/FilterClaude/FilterGemini in the "change the service by changing one line" section.
  • doc/DEPLOYMENT.md: added FilterKimi to the "Supported (external), no gem" table row alongside the other AI filters.
  • doc/VERSIONS: one new bullet under the existing unreleased v26.09 entry.

Tests / Validation

  • New spec/plugins/filter/kimi_spec.rb (24 examples), modeled on sakura_ai_spec.rb's deterministic Net::HTTP.stub(:start) double — no real network access, no real credential. Covers: missing-setting ArgumentErrors validated before any request; description replacement with title/link/order/count preserved; blank-description skip; exact request shape (endpoint, bearer header, JSON body, system/user roles, absence of any sampling/reasoning/tool parameter); success requiring finish_reason == "stop" plus non-empty content; reasoning_content never leaking into the description; permanent failures (invalid JSON, missing choice/message, non-"stop" finish reason, empty content) leaving the description unchanged; retry on network failure/429/5xx and no retry on other 4xx; exhausted retries raising Error; and the token never appearing in logs or exception messages.

Validation run in this checkout (Ruby 3.3.6):

  1. bundle exec rspec spec/plugins/filter/kimi_spec.rbpass, 24 examples, 0 failures.
  2. Full suite (spec/**/*_spec.rb, the pattern rake spec uses) — pass, 447 examples, 0 failures, 0 new skips (only the repository's own pre-existing optional-gem skips: nokogiri, sanitize, fluent-logger, dalli, activerecord). The Kimi spec runs as part of this, not skipped.
  3. gem build automatic.gemspecpass, builds automatic-26.09.gem.
  4. bundle exec ruby -Ilib -e "require 'automatic'"pass, exit 0.
  5. bundle exec bin/automatic --version / --helppass, both exit 0; version prints 26.09.
  6. git diff --checkpass, no output.
  7. No new runtime or dev dependency — pass, Gemfile/automatic.gemspec untouched.
  8. No real API key, voucher code, personal name, or personal email in the diff — pass, only the placeholder YOUR_KIMI_API_KEY and the repository's own existing header contact convention.

Note: this branch (claude/dreamy-sagan-omgzle) is used instead of the originally-specified feature/filter-kimi due to this session's git operating constraints; base master, head claude/dreamy-sagan-omgzle.

🤖 Generated with Claude Code

https://claude.ai/code/session_012batzHd21Na8eJFgc1x2hY


Generated by Claude Code

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012batzHd21Na8eJFgc1x2hY
@id774
id774 merged commit 7677412 into master Sep 11, 2026
8 checks passed
@id774
id774 deleted the claude/dreamy-sagan-omgzle branch September 11, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants