Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions extensions/raycast-ai-custom-providers/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules

# Raycast specific files
raycast-env.d.ts
.raycast-swift-build
.swiftpm
compiled_raycast_swift
compiled_raycast_rust

# misc
.DS_Store
4 changes: 4 additions & 0 deletions extensions/raycast-ai-custom-providers/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"printWidth": 120,
"singleQuote": false
}
5 changes: 5 additions & 0 deletions extensions/raycast-ai-custom-providers/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Raycast AI Custom Providers Changelog

## [Initial Version] - {PR_MERGE_DATE}

- Initial version
133 changes: 133 additions & 0 deletions extensions/raycast-ai-custom-providers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
<p align="center">
<img width=180 src="./assets/extension-icon.png">
</p>

# Custom Provider (Bring Your Own Models)

A Raycast extension that allows you to manage custom AI provider settings through a convenient interface. The extension works with the `providers.yaml` file used by Raycast AI to configure external providers (*see documentation [here](https://manual.raycast.com/ai#Custom%20Providers%20(Bring%20Your%20Own%20Models))*).

## Features

- View a list of all configured providers
- View detailed information about each provider (models, API keys, additional parameters)
- Edit existing or add new providers and models

The extension automatically works with the Raycast AI configuration file located at:
```
~/.config/raycast/ai/providers.yaml
```

> For security purposes, a backup copy of the `providers.yaml` file is created before making changes.

## providers.yaml Structure

The `providers.yaml` file has the following structure:

```yaml
providers:
- id: provider_id # Unique provider identifier (required)
name: Provider Name # Provider name (required)
base_url: https://api.example.com # Base API URL (required)

# API keys (optional)
# If authentication is required, specify at least one key
# If individual models require different keys, specify a separate `key` for each model
api_keys:
provider_key: ENV_VAR_NAME

# Additional parameters for `/chat/completions` endpoint (optional)
additional_parameters:
param1: value1
param2: value2

# List of provider models (required, minimum 1 model)
models:
- id: model_id # Model identifier used by the provider (required)
name: Model Name # Model name in Raycast (required)
provider: provider_key # Mapping to a specific API key (optional)
description: Model description # Model description (optional)
context: 128000 # Context window size (required)

# Model abilities (optional)
# All properties within abilities are also optional
abilities:
temperature:
supported: true
vision:
supported: true
system_message:
supported: true
tools:
supported: false
reasoning_effort:
supported: false
```

### Configuration Examples

#### Simple Provider with One Model

```yaml
providers:
- id: my_provider
name: My Provider
base_url: https://api.example.com
api_keys:
default: MY_API_KEY
models:
- id: gpt-4
name: GPT-4
context: 128000
abilities:
temperature:
supported: true
vision:
supported: true
```

#### Provider with Multiple Models and Different API Keys

```yaml
providers:
- id: multi_provider
name: Multi Provider
base_url: https://api.example.com
api_keys:
openai: OPENAI_KEY
anthropic: ANTHROPIC_KEY
models:
- id: gpt-4o
name: GPT-4o
provider: openai
context: 200000
- id: claude-sonnet
name: Claude Sonnet
provider: anthropic
context: 200000
```

#### Provider Without API Keys

```yaml
providers:
- id: local_provider
name: Local Provider
base_url: http://localhost:4000
models:
- id: local-model
name: Local Model
context: 128000
```

## Important Notes

1. **API Compatibility**: Since the OpenAI API is not a standard, not all providers may work correctly with Raycast AI. It's recommended to check your provider's documentation.

2. **Model Abilities**: If abilities are specified incorrectly, the model may not work correctly in Raycast AI. Always refer to the provider's documentation.

3. **YAML Format**: The extension **DOES NOT** preserve comments and formatting where possible, but when manually editing the file, ensure the YAML syntax is correct.


## License

MIT
91 changes: 91 additions & 0 deletions extensions/raycast-ai-custom-providers/ai-providers.template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
providers:
- id: perplexity
name: Perplexity
base_url: https://api.perplexity.ai
api_keys:
perplexity: PERPLEXITY_KEY
additional_parameters:
return_images: true
web_search_options:
search_context_size: medium
models:
- id: sonar
name: Sonar
provider: perplexity
description: Perplexity AI model for general-purpose queries
context: 128000
abilities:
temperature:
supported: true
vision:
supported: true
system_message:
supported: true
tools:
supported: false
reasoning_effort:
supported: false
- id: sonar-pro
name: Sonar Pro
description: Perplexity AI model for complex queries
context: 200000
abilities:
temperature:
supported: true
vision:
supported: true
system_message:
supported: true
tools:
supported: false
reasoning_effort:
supported: false
- id: my_provider
name: My Provider
base_url: http://localhost:4000
api_keys:
openai: OPENAI_KEY
anthropic: ANTHROPIC_KEY
models:
- id: gpt-4o
name: GPT-4o
context: 200000
provider: openai
abilities:
temperature:
supported: true
vision:
supported: true
system_message:
supported: true
tools:
supported: true
- id: claude-sonnet-4
name: Claude Sonnet 4
context: 200000
provider: anthropic
abilities:
temperature:
supported: true
vision:
supported: true
system_message:
supported: true
tools:
supported: true
- id: litellm
name: LiteLLM
base_url: http://localhost:4000
models:
- id: anthropic/claude-sonnet-4-20250514
name: Claude Sonnet 4
context: 200000
abilities:
temperature:
supported: true
vision:
supported: true
system_message:
supported: true
tools:
supported: true
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
114 changes: 114 additions & 0 deletions extensions/raycast-ai-custom-providers/cursor/code-style.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
alwaysApply: true
---

# Cursor Rules for Raycast Extension Development

## General Development Principles

### Architecture and Data Flow
- **Commands are entry points**: Each command file in `/src` (e.g., `open-repository.tsx`) is a Raycast entry point. It's responsible for initializing state hooks and rendering the main UI view.
- **Views for UI**: UI is organized into `View` components (e.g., `BranchesView.tsx`) located in `/src/components/views/`. Commands render these views as the primary interface.
- **Actions for Operations**: All user operations are implemented as `Action` components in `/src/components/actions/`. They are used within the `ActionPanel` of `View` components.
- **Context for State Passing**: A shared context (containing state hooks and manager instances) is passed down from the command to all child `View` and `Action` components. This avoids prop drilling.
- **Hooks for Logic**: All state management and data fetching logic is encapsulated in custom hooks within `/src/hooks/`.

### Naming Conventions
- **Commands**: `kebab-case` (e.g., `open-repository.tsx`)
- **Components**: `PascalCase` (e.g., `FileActions.tsx`, `BranchesView.tsx`)
- **Utilities**: `kebab-case` (e.g., `data-manager.ts`)
- **Hooks**: `camelCase` with a `use` prefix (e.g., `useRepository.ts`)
- **Types**: `PascalCase` (e.g., `Repository`, `Branch`)
- **Constants**: `UPPER_SNAKE_CASE`

## Raycast API Best Practices

### State Management and Data Fetching
- **Use `useCachedPromise` for Data**: All hooks that fetch data (e.g., `useBranches`, `useStatus`) must use `useCachedPromise` from `@raycast/utils`. The repository path must be part of the dependency array to ensure a separate cache for each repository.
- **Revalidate Data After Mutations**: After any operation that changes repository state, call the `.revalidate()` function on the relevant data hooks (`branches.revalidate()`, `status.revalidate()`) to refresh the UI.
- **Use `useCachedState` for UI State**: For simple, persistent UI state (like filters or dropdown selections), use `useCachedState`.

### Error Handling and Toast Notifications
- **Manager Classes Handle Errors**: Manager classes are responsible for catching errors and displaying failure toasts.
- **Avoid Double Toasts**: `Action` components that call manager classes should not have their own `catch` blocks for showing error toasts, as this would result in duplicate notifications. A `try...catch` block can be used for cleanup or navigation on failure, but should not show a toast for errors already handled by the manager.
- **Use Streaming Toasts for Long Operations**: For long-running commands, manager classes' `outputHandler` provides real-time feedback using an animated toast that updates with `stdout`.
- **Use `showFailureToast` for Error Notifications**: Always use `showFailureToast` from `@raycast/utils` instead of `showToast` with `Toast.Style.Failure`. The `showFailureToast` function automatically handles error type casting and message extraction, eliminating the need for manual `error instanceof Error` checks.
```ts
// Bad: Manual error handling with showToast
showToast({
style: Toast.Style.Failure,
title: "Failed to load data",
message: error instanceof Error ? error.message : "Unknown error",
});

// Good: Use showFailureToast which handles error casting automatically
showFailureToast(error, { title: "Failed to load data" });
```

### Icons and UI
- **Use Both Built-in and Custom Icons**: Use built-in icons from `@raycast/api`'s `Icon` where appropriate. For domain-specific concepts, use custom SVG icons from the `/assets` directory.
- **Create Semantic Icon Components**: For icons that change based on state (e.g., file status, remote provider), create a dedicated component in `/src/components/icons/` (e.g., `StatusIcons.tsx`) that returns the correct icon.

### Confirming Destructive Operations
- **Use `confirmAlert()` for All Destructive Operations**: Any action that results in data loss or is hard to reverse (e.g., deleting a branch, discarding changes) must be wrapped in `confirmAlert()`.
- **Use `Alert.ActionStyle.Destructive`**: The primary confirmation button for a destructive action must use the `Destructive` style.

### Preferences and Settings
- **Use `getPreferenceValues()` Directly**: Access user preferences directly via `getPreferenceValues()` from `@raycast/api`. Do not create wrapper utilities or config files.

## TypeScript and Typing

### JSDoc Comments
- **Document All Public Types**: All exported interfaces, types, and complex functions must have clear JSDoc comments explaining their purpose, properties, and parameters. This is crucial for maintainability.

### Error Handling
- **Safely Access Error Messages**: Always check `error instanceof Error` before accessing `error.message` to avoid runtime errors. Provide a fallback for unknown error types.

### Property Access
```ts
// Bad example: extracts intermediate properties, making the code less clear
const primaryProvider = modelInfo?.providers?.[0];
const inputPrice = primaryProvider?.pricing?.input_per_million;
text={inputPrice} // Not recommended

// Good example: uses full inline property access, preferred for clarity and consistency
text={modelInfo?.providers?.[0]?.pricing?.input_per_million} // Recommended
```
/*
Explanation:
- Bad: Extracting intermediate variables (`primaryProvider`, `inputPrice`) from nested structures makes the data flow less explicit, can lead to code duplication, and hinders maintainability.
- Good: Inline property access keeps the code concise, explicit, and consistent, even if it means duplicating the access path.
*/

### Inline Computations

```ts
// Bad example: extracting even simple single-line computations or ternaries to variables, which reduces code clarity and increases duplication risk
const modelName = cachedModel?.name || model.model_name;
title={modelName} // Not recommended

const markdown = markdownParts.length > 0 ? markdownParts.join("\n\n") : "";
markdown={markdown} // Not recommended

// Good example: keep all single-line logic (ternary, mapping, arithmetic) inline at use site for clarity and consistent style
title={cachedModel?.name || model.model_name} // Recommended
markdown={markdownParts.length > 0 ? markdownParts.join("\n\n") : ""} // Recommended
```
**Explanation:**
- **Bad**: Extracting simple one-liners (ternary, mapping, arithmetic) to variables makes the code less transparent, increases cognitive load, and scatters state and logic.
- **Good**: Always write simple computations inline at the usage site, even if they are duplicated elsewhere. This matches the codebase style and maximizes directness/readability.

**Exception:**
If the logic spans multiple lines, involves branching/complex conditional, or inlining would seriously harm readability, then extract it to a variable as an exception.

## Performance
- **Cache Results**: Rely on `useCachedPromise` to cache the results of expensive operations.

## What NOT to Do
- ❌ Do not use emojis instead of built-in or custom SVG icons.
- ❌ Do not create separate storage managers for frecency data; use `useCachedPromise` or `useCachedState`.
- ❌ Do not create config files for preferences (use the API).
- ❌ Do not create `NavigationActions` (they are available in the Raycast API).
- ❌ Do not use non-English localization in the code; use only English with commonly accepted terminology.
- ❌ Do not use the following shortcut combinations in Actions: `cmd+enter`, `cmd+k`, `cmd+p`.
- ❌ Do not assign a shortcut to the first Action in an ActionPanel.
Loading
Loading