Skip to content

Implement remaining OpenAI-compatible AI stubs#478

Open
Jorel97 wants to merge 13 commits into
profullstack:masterfrom
Jorel97:codex/openai-compatible-ai-stubs-20260529
Open

Implement remaining OpenAI-compatible AI stubs#478
Jorel97 wants to merge 13 commits into
profullstack:masterfrom
Jorel97:codex/openai-compatible-ai-stubs-20260529

Conversation

@Jorel97
Copy link
Copy Markdown

@Jorel97 Jorel97 commented May 29, 2026

Implements the remaining OpenAI-compatible AI stubs from #450:

  • Kimi/Moonshot
  • Cohere compatibility API
  • Novita OpenAI API
  • Venice API
  • Parasail serverless chat API

Each adapter now:

  • preserves dry-run short-circuiting
  • sends system/user messages to /v1/chat/completions
  • forwards maxTokens, temperature, and opts.extra
  • maps response text, model, and usage tokens
  • reports provider status and the first response-body excerpt on errors

Verification:

  • Static implementation matched against packages/ai/openai/src/index.ts
  • Provider base URLs checked against vendor docs linked in each adapter setup

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR promotes five AI adapter stubs (Cohere, Kimi/Moonshot, NovitaAI, Parasail, Venice) to full OpenAI-compatible implementations, and adds a mergeAdapterConfig utility to setup.ts so that re-running setup can correctly preserve or clear optional config fields (e.g. baseUrl) without clobbering the stored value.

  • Each adapter adds a chatCompletionsUrl helper, wires generate with dry-run short-circuit, sends system/user messages to /v1/chat/completions, and maps response text, model, and usage tokens.
  • setup-helpers.ts stores an empty-string sentinel ('') when a user leaves a non-secret optional field blank, so downstream code can detect and delete stale values.
  • setup.ts introduces mergeAdapterConfig to shallow-merge the incoming result config over the existing stored config, deleting keys whose value is '' — but the early-return branch fires when existing is undefined (first-time setup), causing the empty sentinel to be persisted instead of discarded.

Confidence Score: 4/5

The five adapter implementations are correct and safe to ship; the config-merge fix has one gap in setup.ts that causes a broken URL on first-time use when baseUrl is left blank.

When an adapter has never been configured, getAdapterConfig returns undefined. mergeAdapterConfig(undefined, { baseUrl: '' }) exits early and writes { baseUrl: '' } to disk. The ?? operator in every generate function does not coalesce empty strings, so chatCompletionsUrl('') produces the relative path /v1/chat/completions, and Node.js fetch throws TypeError: Invalid URL on the first generation attempt after a fresh setup where the user left baseUrl blank.

packages/core/src/setup.ts — the mergeAdapterConfig function needs to treat a missing existing config the same as an empty object rather than bypassing the cleanup loop.

Important Files Changed

Filename Overview
packages/core/src/setup.ts Adds mergeAdapterConfig to preserve/clear stored adapter config on re-setup; the merge logic has a gap when no previous config exists (returns next verbatim, preserving empty-string sentinel values instead of discarding them).
packages/core/src/setup-helpers.ts Adds empty-string sentinel (configExtras[field.key] = '') for blank non-secret optional fields so mergeAdapterConfig knows to delete stale values; correct in the re-setup case but the sentinel leaks through when there is no existing config.
packages/ai/cohere/src/index.ts Replaces stub with a working OpenAI-compatible generate implementation; preserves dry-run, forwards all standard opts, and maps response/usage fields correctly.
packages/ai/kimi/src/index.ts Replaces stub with Moonshot/Kimi OpenAI-compatible adapter; mirrors the cohere pattern faithfully.
packages/ai/novita/src/index.ts Replaces stub with NovitaAI OpenAI-compatible adapter; default base https://api.novita.ai/openai will produce the endpoint https://api.novita.ai/openai/v1/chat/completions.
packages/ai/parasail/src/index.ts Fixes the badly-corrupted stub (wrong secretKey, wrong defaultModel) and implements the adapter; intentionally uses max_completion_tokens instead of max_tokens, which differs from the other four adapters.
packages/ai/venice/src/index.ts Replaces stub with Venice AI OpenAI-compatible adapter; mirrors the cohere pattern.

Sequence Diagram

sequenceDiagram
    participant User
    participant tokenSetup
    participant runSetup
    participant mergeAdapterConfig
    participant ConfigStore
    participant generate

    User->>tokenSetup: run setup (first time, leave baseUrl blank)
    tokenSetup->>tokenSetup: "configExtras['baseUrl'] = ''"
    tokenSetup-->>runSetup: "{ ok: true, config: { baseUrl: '' } }"

    runSetup->>ConfigStore: getAdapterConfig(adapter.id)
    ConfigStore-->>runSetup: undefined (no prior config)

    runSetup->>mergeAdapterConfig: "mergeAdapterConfig(undefined, { baseUrl: '' })"
    Note over mergeAdapterConfig: isPlainRecord(undefined) → false<br/>early-return: returns next as-is
    mergeAdapterConfig-->>runSetup: "{ baseUrl: '' }"

    runSetup->>ConfigStore: "setAdapterConfig(id, { baseUrl: '' })"

    User->>generate: generate(ctx, prompt, opts, config)
    Note over generate: config.baseUrl = ''<br/>'' ?? DEFAULT_BASE = ''
    generate->>generate: chatCompletionsUrl('') → '/v1/chat/completions'
    generate->>generate: fetch('/v1/chat/completions') → TypeError: Invalid URL ❌
Loading

Reviews (3): Last reviewed commit: "fix(setup): allow clearing optional setu..." | Re-trigger Greptile

Comment on lines +9 to +12
function chatCompletionsUrl(baseUrl: string): string {
const base = baseUrl.replace(/\/+$/, '');
return base.endsWith('/v1') ? `${base}/chat/completions` : `${base}/v1/chat/completions`;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 chatCompletionsUrl duplicated verbatim in all five adapters

The exact same function is copy-pasted into cohere, kimi, novita, parasail, and venice. Any future fix must be applied in five places. Extracting it once into @profullstack/sh1pt-core alongside the existing tokenSetup / defineAi exports would make it reusable and keep these thin adapters truly thin.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Comment thread packages/ai/cohere/src/index.ts
@Jorel97
Copy link
Copy Markdown
Author

Jorel97 commented May 29, 2026

Addressed the Greptile setup-config concern in 4185190: runSetup now merges a returned plain-object setup config over any existing adapter config before saving, so tokenSetup/webhookSetup reuse paths no longer wipe previously stored non-secret fields like baseUrl. I also kept the provider-doc follow-up (Cohere current compatibility model and Parasail max_completion_tokens mapping).

Comment thread packages/core/src/setup.ts
@Jorel97
Copy link
Copy Markdown
Author

Jorel97 commented May 29, 2026

Addressed the latest Greptile note in 6a8c517: tokenSetup now returns an explicit empty string for blank non-secret optional fields, and runSetup treats that as a delete marker when merging adapter config. Reuse still returns an empty config object and preserves existing values, while a fresh re-run with blank baseUrl now clears the override and falls back to provider defaults as prompted.

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.

1 participant