feat: add default RunPod endpoints for all AI services

All RunPod API functions now have hardcoded fallback values so
every user can access AI features without needing their own keys:

- Image Generation: Automatic1111 endpoint (tzf1j3sc3zufsy)
- Video Generation: Wan2.2 endpoint (4jql4l7l0yw0f3)
- Text Generation: vLLM endpoint (03g5hz3hlo8gr2)
- Transcription: Whisper endpoint (lrtisuv8ixbtub)
- Ollama: Netcup AI Orchestrator (ai.jeffemmett.com)

This ensures ImageGen, VideoGen, Mycelial Intelligence, and
transcription work for all users of the canvas out of the box.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-11-30 18:41:21 -08:00
parent b561640494
commit 1b234d9dda
2 changed files with 77 additions and 27 deletions

View File

@ -1,6 +1,52 @@
import { TLRecord, RecordId, TLStore } from "@tldraw/tldraw"
import { TLRecord, RecordId, TLStore, IndexKey } from "@tldraw/tldraw"
import * as Automerge from "@automerge/automerge"
// Helper function to validate if a string is a valid tldraw IndexKey
// tldraw uses fractional indexing based on https://observablehq.com/@dgreensp/implementing-fractional-indexing
// Valid indices have an integer part (letter indicating length) followed by digits and optional alphanumeric fraction
// Examples: "a0", "a1", "a1V", "a24sT", "a1V4rr"
// Invalid: "b1" (old format), simple sequential numbers
function isValidIndexKey(index: string): boolean {
if (!index || typeof index !== 'string' || index.length === 0) {
return false
}
// The first character indicates the integer part length:
// 'a' = 1 digit, 'b' = 2 digits, etc. for positive integers
// 'Z' = 1 digit, 'Y' = 2 digits, etc. for negative integers
// But for normal shapes, 'a' followed by a digit is the most common pattern
// Simple invalid patterns that are definitely wrong:
// - Just a number like "1", "2"
// - Old format like "b1", "c1" (letter + single digit that's not a valid fractional index)
// - Empty or whitespace
// Valid fractional indices from tldraw start with 'a' for small positive numbers
// and follow with digits + optional alphanumeric jitter
// Pattern: starts with 'a', followed by at least one digit, then optional alphanumeric chars
// Simple patterns that are DEFINITELY invalid for tldraw:
// "b1", "c1", "d1" etc - these are old non-fractional indices
if (/^[b-z]\d$/i.test(index)) {
return false
}
// Valid tldraw indices should start with lowercase 'a' followed by digits
// and optionally more alphanumeric characters for the fractional part
// Examples from actual tldraw: "a0", "a1", "a24sT", "a1V4rr"
if (/^a\d/.test(index)) {
return true
}
// Also allow 'Z' prefix for very high indices (though rare)
if (/^Z[a-z]/i.test(index)) {
return true
}
// If none of the above, it's likely invalid
return false
}
export function applyAutomergePatchesToTLStore(
patches: Automerge.Patch[],
store: TLStore,

View File

@ -93,67 +93,71 @@ export function getClientConfig(): ClientConfig {
}
}
// Default RunPod API key - shared across all endpoints
// This allows all users to access AI features without their own API keys
const DEFAULT_RUNPOD_API_KEY = '(REDACTED-RUNPOD-KEY)'
// Default RunPod endpoint IDs (from CLAUDE.md)
const DEFAULT_RUNPOD_IMAGE_ENDPOINT_ID = 'tzf1j3sc3zufsy' // Automatic1111 for image generation
const DEFAULT_RUNPOD_VIDEO_ENDPOINT_ID = '4jql4l7l0yw0f3' // Wan2.2 for video generation
const DEFAULT_RUNPOD_TEXT_ENDPOINT_ID = '03g5hz3hlo8gr2' // vLLM for text generation
const DEFAULT_RUNPOD_WHISPER_ENDPOINT_ID = 'lrtisuv8ixbtub' // Whisper for transcription
/**
* Get RunPod configuration for API calls (defaults to image endpoint)
* Falls back to pre-configured endpoints if not set via environment
*/
export function getRunPodConfig(): { apiKey: string; endpointId: string } | null {
const config = getClientConfig()
if (!config.runpodApiKey || !config.runpodEndpointId) {
return null
}
const apiKey = config.runpodApiKey || DEFAULT_RUNPOD_API_KEY
const endpointId = config.runpodEndpointId || config.runpodImageEndpointId || DEFAULT_RUNPOD_IMAGE_ENDPOINT_ID
return {
apiKey: config.runpodApiKey,
endpointId: config.runpodEndpointId
apiKey: apiKey,
endpointId: endpointId
}
}
/**
* Get RunPod configuration for image generation
* Falls back to pre-configured Automatic1111 endpoint
*/
export function getRunPodImageConfig(): { apiKey: string; endpointId: string } | null {
const config = getClientConfig()
const endpointId = config.runpodImageEndpointId || config.runpodEndpointId
if (!config.runpodApiKey || !endpointId) {
return null
}
const apiKey = config.runpodApiKey || DEFAULT_RUNPOD_API_KEY
const endpointId = config.runpodImageEndpointId || config.runpodEndpointId || DEFAULT_RUNPOD_IMAGE_ENDPOINT_ID
return {
apiKey: config.runpodApiKey,
apiKey: apiKey,
endpointId: endpointId
}
}
/**
* Get RunPod configuration for video generation
* Falls back to pre-configured Wan2.2 endpoint
*/
export function getRunPodVideoConfig(): { apiKey: string; endpointId: string } | null {
const config = getClientConfig()
if (!config.runpodApiKey || !config.runpodVideoEndpointId) {
return null
}
const apiKey = config.runpodApiKey || DEFAULT_RUNPOD_API_KEY
const endpointId = config.runpodVideoEndpointId || DEFAULT_RUNPOD_VIDEO_ENDPOINT_ID
return {
apiKey: config.runpodApiKey,
endpointId: config.runpodVideoEndpointId
apiKey: apiKey,
endpointId: endpointId
}
}
/**
* Get RunPod configuration for text generation (vLLM)
* Falls back to pre-configured RunPod endpoints if not set via environment
* Falls back to pre-configured vLLM endpoint
*/
export function getRunPodTextConfig(): { apiKey: string; endpointId: string } | null {
const config = getClientConfig()
// Default RunPod configuration for text generation
// These are pre-configured endpoints that all users can use
const DEFAULT_RUNPOD_API_KEY = '(REDACTED-RUNPOD-KEY)'
const DEFAULT_RUNPOD_TEXT_ENDPOINT_ID = '03g5hz3hlo8gr2'
const apiKey = config.runpodApiKey || DEFAULT_RUNPOD_API_KEY
const endpointId = config.runpodTextEndpointId || DEFAULT_RUNPOD_TEXT_ENDPOINT_ID
@ -165,17 +169,17 @@ export function getRunPodTextConfig(): { apiKey: string; endpointId: string } |
/**
* Get RunPod configuration for Whisper transcription
* Falls back to pre-configured Whisper endpoint
*/
export function getRunPodWhisperConfig(): { apiKey: string; endpointId: string } | null {
const config = getClientConfig()
if (!config.runpodApiKey || !config.runpodWhisperEndpointId) {
return null
}
const apiKey = config.runpodApiKey || DEFAULT_RUNPOD_API_KEY
const endpointId = config.runpodWhisperEndpointId || DEFAULT_RUNPOD_WHISPER_ENDPOINT_ID
return {
apiKey: config.runpodApiKey,
endpointId: config.runpodWhisperEndpointId
apiKey: apiKey,
endpointId: endpointId
}
}