@quilltap/qtap-plugin-gab-ai 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +46 -0
- package/README.md +136 -0
- package/esbuild.config.mjs +109 -0
- package/icon.tsx +50 -0
- package/index.js +255 -0
- package/index.ts +232 -0
- package/manifest.json +56 -0
- package/package.json +22 -0
- package/provider.ts +31 -0
- package/types.ts +22 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Package
|
|
6
|
+
|
|
7
|
+
Published as `@quilltap/qtap-plugin-gab-ai` (public npm package).
|
|
8
|
+
|
|
9
|
+
## Build Commands
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm run build # Bundle TypeScript to index.js using esbuild
|
|
13
|
+
npm run clean # Remove built index.js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Architecture
|
|
17
|
+
|
|
18
|
+
This is a Quilltap LLM provider plugin that integrates Gab AI's API. The plugin uses an OpenAI-compatible API pattern.
|
|
19
|
+
|
|
20
|
+
### Key Files
|
|
21
|
+
|
|
22
|
+
- **index.ts** - Plugin entry point exporting the `LLMProviderPlugin` interface with metadata, config, capabilities, and factory methods
|
|
23
|
+
- **provider.ts** - `GabAIProvider` class extending `OpenAICompatibleProvider` from `@quilltap/plugin-utils` with Gab-specific config (base URL: `https://gab.ai/v1`)
|
|
24
|
+
- **types.ts** - Re-exports types from `@quilltap/plugin-types`
|
|
25
|
+
- **manifest.json** - Plugin metadata including compatibility requirements (Quilltap >=1.7.0, Node >=18.0.0)
|
|
26
|
+
- **icon.tsx** - React component for the provider icon
|
|
27
|
+
|
|
28
|
+
### Build System
|
|
29
|
+
|
|
30
|
+
esbuild bundles the plugin to CommonJS format. External packages (`@quilltap/plugin-types`, `@quilltap/plugin-utils`, React, Next.js, zod) are provided at runtime by the main Quilltap app and should not be bundled.
|
|
31
|
+
|
|
32
|
+
### Plugin Interface
|
|
33
|
+
|
|
34
|
+
The plugin exports:
|
|
35
|
+
- `metadata` - Provider name, display name, colors, abbreviation
|
|
36
|
+
- `config` - API key requirements
|
|
37
|
+
- `capabilities` - Supports chat only (no image generation, embeddings, or web search)
|
|
38
|
+
- `attachmentSupport` - Text-only, no file attachments
|
|
39
|
+
- `createProvider()` - Factory for GabAIProvider instances
|
|
40
|
+
- `getAvailableModels()` - Fetches models from Gab AI API
|
|
41
|
+
- `validateApiKey()` - Validates API credentials
|
|
42
|
+
- `formatTools()` / `parseToolCalls()` - OpenAI-format tool handling
|
|
43
|
+
|
|
44
|
+
### Dependencies
|
|
45
|
+
|
|
46
|
+
Runtime dependencies (`@quilltap/plugin-utils`, React, Next.js, zod) are provided by the Quilltap host application. The OpenAI SDK is provided transitively via `@quilltap/plugin-utils`.
|
package/README.md
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Gab AI Provider Plugin for Quilltap
|
|
2
|
+
|
|
3
|
+
This plugin provides integration with Gab AI's API, enabling Quilltap to use Gab AI language models for chat completions.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Chat Completions**: Access to Gab AI language models for conversational AI
|
|
8
|
+
- **Streaming**: Support for streaming responses for real-time chat
|
|
9
|
+
- **Text-Only**: Text input and output (no file attachments)
|
|
10
|
+
- **OpenAI-Compatible**: Uses OpenAI SDK with Gab AI's base URL
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
The plugin is included with Quilltap. To ensure you have the latest version of the OpenAI SDK (used for API compatibility):
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install openai@latest
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Configuration
|
|
21
|
+
|
|
22
|
+
### API Key Setup
|
|
23
|
+
|
|
24
|
+
1. Create a Gab AI account at https://api.gab.com
|
|
25
|
+
2. Generate an API key from your account dashboard
|
|
26
|
+
3. In Quilltap settings, add your API key under the Gab AI provider configuration
|
|
27
|
+
|
|
28
|
+
### Required Permissions
|
|
29
|
+
|
|
30
|
+
This plugin requires the following:
|
|
31
|
+
- Network access to `api.gab.com`
|
|
32
|
+
- A valid Gab AI API account with billing enabled
|
|
33
|
+
|
|
34
|
+
## Supported Models
|
|
35
|
+
|
|
36
|
+
The available models depend on your Gab AI account access. Use the "Fetch Available Models" feature in Quilltap to see your accessible models.
|
|
37
|
+
|
|
38
|
+
### Chat Completion Models
|
|
39
|
+
|
|
40
|
+
Gab AI provides various language models optimized for different use cases:
|
|
41
|
+
- Standard models for general-purpose chat
|
|
42
|
+
- Specialized models for specific domains
|
|
43
|
+
|
|
44
|
+
**Note**: The exact available models and their capabilities depend on your Gab AI account access and may change over time. Use the "Fetch Available Models" feature in Quilltap to see your current accessible models.
|
|
45
|
+
|
|
46
|
+
## File Attachment Support
|
|
47
|
+
|
|
48
|
+
The plugin does not support file attachments. All interactions are text-only.
|
|
49
|
+
|
|
50
|
+
### Supported MIME Types
|
|
51
|
+
|
|
52
|
+
None - text only
|
|
53
|
+
|
|
54
|
+
## Parameters
|
|
55
|
+
|
|
56
|
+
### Chat Completion Parameters
|
|
57
|
+
|
|
58
|
+
- **model**: The model to use (varies by account access)
|
|
59
|
+
- **temperature**: Randomness of responses (0-2, default: 0.7)
|
|
60
|
+
- **maxTokens**: Maximum response length (default: 1000)
|
|
61
|
+
- **topP**: Diversity parameter (0-1, default: 1)
|
|
62
|
+
- **stop**: Stop sequences to end generation
|
|
63
|
+
|
|
64
|
+
## Logging
|
|
65
|
+
|
|
66
|
+
The plugin includes comprehensive debug logging for all operations:
|
|
67
|
+
|
|
68
|
+
- API calls and responses
|
|
69
|
+
- Stream processing
|
|
70
|
+
- Model fetching
|
|
71
|
+
- API key validation
|
|
72
|
+
- Error handling
|
|
73
|
+
|
|
74
|
+
Set `LOG_LEVEL=debug` to see detailed operation logs.
|
|
75
|
+
|
|
76
|
+
## Error Handling
|
|
77
|
+
|
|
78
|
+
The plugin provides detailed error messages for:
|
|
79
|
+
- Invalid API keys
|
|
80
|
+
- Model availability errors
|
|
81
|
+
- API errors and rate limiting
|
|
82
|
+
- Network connectivity issues
|
|
83
|
+
|
|
84
|
+
## Examples
|
|
85
|
+
|
|
86
|
+
### Basic Chat
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const response = await provider.sendMessage({
|
|
90
|
+
messages: [
|
|
91
|
+
{ role: 'user', content: 'Hello, how are you?' }
|
|
92
|
+
],
|
|
93
|
+
model: 'gab-01',
|
|
94
|
+
}, apiKey);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Streaming
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
for await (const chunk of provider.streamMessage({
|
|
101
|
+
messages: [{ role: 'user', content: 'Tell me a story' }],
|
|
102
|
+
model: 'gab-01',
|
|
103
|
+
}, apiKey)) {
|
|
104
|
+
console.log(chunk.content);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Troubleshooting
|
|
109
|
+
|
|
110
|
+
### Invalid API Key
|
|
111
|
+
|
|
112
|
+
- Verify the key is correct from your Gab AI account dashboard
|
|
113
|
+
- Ensure the key has not expired
|
|
114
|
+
- Check that billing is enabled on your Gab AI account
|
|
115
|
+
|
|
116
|
+
### No Models Available
|
|
117
|
+
|
|
118
|
+
- Ensure your API key has access to the models
|
|
119
|
+
- Check that your account is not in a restricted region
|
|
120
|
+
- Verify your billing information is valid
|
|
121
|
+
|
|
122
|
+
### Slow Responses
|
|
123
|
+
|
|
124
|
+
- Check Gab AI status page
|
|
125
|
+
- Verify network connection
|
|
126
|
+
- Check rate limiting (429 errors)
|
|
127
|
+
|
|
128
|
+
## Support
|
|
129
|
+
|
|
130
|
+
For issues with the plugin, refer to:
|
|
131
|
+
- Quilltap GitHub: https://github.com/foundry-9/F9-Quilltap
|
|
132
|
+
- Gab AI Documentation: https://api.gab.com/docs
|
|
133
|
+
|
|
134
|
+
## License
|
|
135
|
+
|
|
136
|
+
MIT License - See LICENSE file for details
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* esbuild configuration for qtap-plugin-anthropic
|
|
3
|
+
*
|
|
4
|
+
* Bundles the plugin with its SDK dependency into a single CommonJS file.
|
|
5
|
+
* External packages (react, zod, etc.) are provided by the main app at runtime.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import * as esbuild from 'esbuild';
|
|
9
|
+
import { resolve, dirname } from 'path';
|
|
10
|
+
import { fileURLToPath } from 'url';
|
|
11
|
+
|
|
12
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
|
|
14
|
+
// Find the project root (3 levels up from plugin directory)
|
|
15
|
+
const projectRoot = resolve(__dirname, '..', '..', '..');
|
|
16
|
+
|
|
17
|
+
// Packages that should NOT be bundled - they're provided by the main app at runtime
|
|
18
|
+
const EXTERNAL_PACKAGES = [
|
|
19
|
+
// Quilltap packages (provided by main app)
|
|
20
|
+
'@quilltap/plugin-types',
|
|
21
|
+
'@quilltap/plugin-utils',
|
|
22
|
+
// React (provided by main app)
|
|
23
|
+
'react',
|
|
24
|
+
'react-dom',
|
|
25
|
+
'react/jsx-runtime',
|
|
26
|
+
'react/jsx-dev-runtime',
|
|
27
|
+
// Next.js (provided by main app)
|
|
28
|
+
'next',
|
|
29
|
+
'next-auth',
|
|
30
|
+
// Other main app dependencies
|
|
31
|
+
'zod',
|
|
32
|
+
// Node.js built-ins
|
|
33
|
+
'fs',
|
|
34
|
+
'path',
|
|
35
|
+
'crypto',
|
|
36
|
+
'http',
|
|
37
|
+
'https',
|
|
38
|
+
'url',
|
|
39
|
+
'util',
|
|
40
|
+
'stream',
|
|
41
|
+
'events',
|
|
42
|
+
'buffer',
|
|
43
|
+
'querystring',
|
|
44
|
+
'os',
|
|
45
|
+
'child_process',
|
|
46
|
+
'node:fs',
|
|
47
|
+
'node:path',
|
|
48
|
+
'node:crypto',
|
|
49
|
+
'node:http',
|
|
50
|
+
'node:https',
|
|
51
|
+
'node:url',
|
|
52
|
+
'node:util',
|
|
53
|
+
'node:stream',
|
|
54
|
+
'node:events',
|
|
55
|
+
'node:buffer',
|
|
56
|
+
'node:querystring',
|
|
57
|
+
'node:os',
|
|
58
|
+
'node:child_process',
|
|
59
|
+
'node:module',
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
async function build() {
|
|
63
|
+
try {
|
|
64
|
+
const result = await esbuild.build({
|
|
65
|
+
entryPoints: [resolve(__dirname, 'index.ts')],
|
|
66
|
+
bundle: true,
|
|
67
|
+
platform: 'node',
|
|
68
|
+
target: 'node18',
|
|
69
|
+
format: 'cjs',
|
|
70
|
+
outfile: resolve(__dirname, 'index.js'),
|
|
71
|
+
|
|
72
|
+
// Resolve @/ imports to project root
|
|
73
|
+
alias: {
|
|
74
|
+
'@': projectRoot,
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
// Don't bundle these - they're available at runtime from the main app
|
|
78
|
+
external: EXTERNAL_PACKAGES,
|
|
79
|
+
|
|
80
|
+
// Source maps for debugging (optional, can remove for smaller builds)
|
|
81
|
+
sourcemap: false,
|
|
82
|
+
|
|
83
|
+
// Minification (optional, disable for debugging)
|
|
84
|
+
minify: false,
|
|
85
|
+
|
|
86
|
+
// Tree shaking
|
|
87
|
+
treeShaking: true,
|
|
88
|
+
|
|
89
|
+
// Log level
|
|
90
|
+
logLevel: 'info',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (result.errors.length > 0) {
|
|
94
|
+
console.error('Build failed with errors:', result.errors);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log('Build completed successfully!');
|
|
99
|
+
|
|
100
|
+
if (result.warnings.length > 0) {
|
|
101
|
+
console.warn('Warnings:', result.warnings);
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error('Build failed:', error);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
build();
|
package/icon.tsx
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import React from 'react';
|
|
4
|
+
|
|
5
|
+
interface IconProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Gab AI Icon Component
|
|
11
|
+
* Displays the Gab AI branding icon with customizable styling
|
|
12
|
+
*
|
|
13
|
+
* @param props Icon component properties
|
|
14
|
+
* @param props.className Optional CSS class name for styling
|
|
15
|
+
* @returns JSX Element representing the Gab AI icon
|
|
16
|
+
*/
|
|
17
|
+
export function GabAIIcon({ className = 'h-5 w-5' }: IconProps) {
|
|
18
|
+
return (
|
|
19
|
+
<svg
|
|
20
|
+
className={`text-green-600 ${className}`}
|
|
21
|
+
fill="currentColor"
|
|
22
|
+
viewBox="0 0 24 24"
|
|
23
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
24
|
+
data-testid="gab-ai-icon"
|
|
25
|
+
>
|
|
26
|
+
{/* Gab AI logo - stylized circular design */}
|
|
27
|
+
<circle cx="12" cy="12" r="11" fill="none" stroke="currentColor" strokeWidth="2" />
|
|
28
|
+
<path
|
|
29
|
+
d="M12 2A10 10 0 1 1 2 12A10 10 0 0 1 12 2Z"
|
|
30
|
+
fill="currentColor"
|
|
31
|
+
opacity="0.1"
|
|
32
|
+
/>
|
|
33
|
+
{/* Text label GAB */}
|
|
34
|
+
<text
|
|
35
|
+
x="50%"
|
|
36
|
+
y="50%"
|
|
37
|
+
textAnchor="middle"
|
|
38
|
+
dominantBaseline="middle"
|
|
39
|
+
fill="currentColor"
|
|
40
|
+
fontSize="9"
|
|
41
|
+
fontWeight="bold"
|
|
42
|
+
fontFamily="system-ui, -apple-system, sans-serif"
|
|
43
|
+
>
|
|
44
|
+
GAB
|
|
45
|
+
</text>
|
|
46
|
+
</svg>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default GabAIIcon;
|
package/index.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// index.ts
|
|
30
|
+
var index_exports = {};
|
|
31
|
+
__export(index_exports, {
|
|
32
|
+
default: () => index_default,
|
|
33
|
+
plugin: () => plugin
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// provider.ts
|
|
38
|
+
var import_plugin_utils = require("@quilltap/plugin-utils");
|
|
39
|
+
var GabAIProvider = class extends import_plugin_utils.OpenAICompatibleProvider {
|
|
40
|
+
constructor() {
|
|
41
|
+
super({
|
|
42
|
+
baseUrl: "https://gab.ai/v1",
|
|
43
|
+
providerName: "GabAI",
|
|
44
|
+
requireApiKey: true,
|
|
45
|
+
attachmentErrorMessage: "Gab AI does not support file attachments"
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// icon.tsx
|
|
51
|
+
var import_react = __toESM(require("react"));
|
|
52
|
+
function GabAIIcon({ className = "h-5 w-5" }) {
|
|
53
|
+
return /* @__PURE__ */ import_react.default.createElement(
|
|
54
|
+
"svg",
|
|
55
|
+
{
|
|
56
|
+
className: `text-green-600 ${className}`,
|
|
57
|
+
fill: "currentColor",
|
|
58
|
+
viewBox: "0 0 24 24",
|
|
59
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
60
|
+
"data-testid": "gab-ai-icon"
|
|
61
|
+
},
|
|
62
|
+
/* @__PURE__ */ import_react.default.createElement("circle", { cx: "12", cy: "12", r: "11", fill: "none", stroke: "currentColor", strokeWidth: "2" }),
|
|
63
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
64
|
+
"path",
|
|
65
|
+
{
|
|
66
|
+
d: "M12 2A10 10 0 1 1 2 12A10 10 0 0 1 12 2Z",
|
|
67
|
+
fill: "currentColor",
|
|
68
|
+
opacity: "0.1"
|
|
69
|
+
}
|
|
70
|
+
),
|
|
71
|
+
/* @__PURE__ */ import_react.default.createElement(
|
|
72
|
+
"text",
|
|
73
|
+
{
|
|
74
|
+
x: "50%",
|
|
75
|
+
y: "50%",
|
|
76
|
+
textAnchor: "middle",
|
|
77
|
+
dominantBaseline: "middle",
|
|
78
|
+
fill: "currentColor",
|
|
79
|
+
fontSize: "9",
|
|
80
|
+
fontWeight: "bold",
|
|
81
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
82
|
+
},
|
|
83
|
+
"GAB"
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// index.ts
|
|
89
|
+
var import_plugin_utils2 = require("@quilltap/plugin-utils");
|
|
90
|
+
var logger = (0, import_plugin_utils2.createPluginLogger)("GabAI");
|
|
91
|
+
var metadata = {
|
|
92
|
+
providerName: "GAB_AI",
|
|
93
|
+
displayName: "Gab AI",
|
|
94
|
+
description: "Gab AI language models for chat completions",
|
|
95
|
+
colors: {
|
|
96
|
+
bg: "bg-green-100",
|
|
97
|
+
text: "text-green-800",
|
|
98
|
+
icon: "text-green-600"
|
|
99
|
+
},
|
|
100
|
+
abbreviation: "GAB"
|
|
101
|
+
};
|
|
102
|
+
var config = {
|
|
103
|
+
requiresApiKey: true,
|
|
104
|
+
requiresBaseUrl: false,
|
|
105
|
+
apiKeyLabel: "Gab AI API Key"
|
|
106
|
+
};
|
|
107
|
+
var capabilities = {
|
|
108
|
+
chat: true,
|
|
109
|
+
imageGeneration: false,
|
|
110
|
+
embeddings: false,
|
|
111
|
+
webSearch: false
|
|
112
|
+
};
|
|
113
|
+
var attachmentSupport = {
|
|
114
|
+
supportsAttachments: false,
|
|
115
|
+
supportedMimeTypes: [],
|
|
116
|
+
description: "No attachment support (text only)",
|
|
117
|
+
notes: "Gab AI does not currently support file attachments"
|
|
118
|
+
};
|
|
119
|
+
var plugin = {
|
|
120
|
+
metadata,
|
|
121
|
+
config,
|
|
122
|
+
capabilities,
|
|
123
|
+
attachmentSupport,
|
|
124
|
+
/**
|
|
125
|
+
* Factory method to create a Gab AI LLM provider instance
|
|
126
|
+
*/
|
|
127
|
+
createProvider: (baseUrl) => {
|
|
128
|
+
logger.debug("Creating Gab AI provider instance", { context: "plugin.createProvider", baseUrl });
|
|
129
|
+
return new GabAIProvider();
|
|
130
|
+
},
|
|
131
|
+
/**
|
|
132
|
+
* Get list of available models from Gab AI API
|
|
133
|
+
* Requires a valid API key
|
|
134
|
+
*/
|
|
135
|
+
getAvailableModels: async (apiKey, baseUrl) => {
|
|
136
|
+
logger.debug("Fetching available Gab AI models", { context: "plugin.getAvailableModels" });
|
|
137
|
+
try {
|
|
138
|
+
const provider = new GabAIProvider();
|
|
139
|
+
const models = await provider.getAvailableModels(apiKey);
|
|
140
|
+
logger.debug("Successfully fetched Gab AI models", { context: "plugin.getAvailableModels", count: models.length });
|
|
141
|
+
return models;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
logger.error("Failed to fetch Gab AI models", { context: "plugin.getAvailableModels" }, error instanceof Error ? error : void 0);
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
/**
|
|
148
|
+
* Validate a Gab AI API key
|
|
149
|
+
*/
|
|
150
|
+
validateApiKey: async (apiKey, baseUrl) => {
|
|
151
|
+
logger.debug("Validating Gab AI API key", { context: "plugin.validateApiKey" });
|
|
152
|
+
try {
|
|
153
|
+
const provider = new GabAIProvider();
|
|
154
|
+
const isValid = await provider.validateApiKey(apiKey);
|
|
155
|
+
logger.debug("Gab AI API key validation result", { context: "plugin.validateApiKey", isValid });
|
|
156
|
+
return isValid;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
logger.error("Error validating Gab AI API key", { context: "plugin.validateApiKey" }, error instanceof Error ? error : void 0);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
/**
|
|
163
|
+
* Get static model information
|
|
164
|
+
* Returns cached information about Gab AI models without needing API calls
|
|
165
|
+
*/
|
|
166
|
+
getModelInfo: () => {
|
|
167
|
+
logger.debug("Getting Gab AI model information", { context: "plugin.getModelInfo" });
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
id: "gab-01",
|
|
171
|
+
name: "Gab AI 01",
|
|
172
|
+
contextWindow: 128e3,
|
|
173
|
+
maxOutputTokens: 4096,
|
|
174
|
+
supportsImages: false,
|
|
175
|
+
supportsTools: false
|
|
176
|
+
}
|
|
177
|
+
];
|
|
178
|
+
},
|
|
179
|
+
/**
|
|
180
|
+
* Render the Gab AI icon
|
|
181
|
+
*/
|
|
182
|
+
renderIcon: (props) => {
|
|
183
|
+
logger.debug("Rendering Gab AI icon", { context: "plugin.renderIcon", className: props.className });
|
|
184
|
+
return GabAIIcon(props);
|
|
185
|
+
},
|
|
186
|
+
/**
|
|
187
|
+
* Format tools from OpenAI format to OpenAI format
|
|
188
|
+
* Gab AI uses OpenAI format (inherited from OpenAI-compatible)
|
|
189
|
+
*
|
|
190
|
+
* @param tools Array of tools in OpenAI format
|
|
191
|
+
* @returns Array of tools in OpenAI format
|
|
192
|
+
*/
|
|
193
|
+
formatTools: (tools) => {
|
|
194
|
+
logger.debug("Formatting tools for Gab AI provider", {
|
|
195
|
+
context: "plugin.formatTools",
|
|
196
|
+
toolCount: tools.length
|
|
197
|
+
});
|
|
198
|
+
try {
|
|
199
|
+
const formattedTools = [];
|
|
200
|
+
for (const tool of tools) {
|
|
201
|
+
if (!("function" in tool)) {
|
|
202
|
+
logger.warn("Skipping tool with invalid format", {
|
|
203
|
+
context: "plugin.formatTools"
|
|
204
|
+
});
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
formattedTools.push(tool);
|
|
208
|
+
}
|
|
209
|
+
logger.debug("Successfully formatted tools", {
|
|
210
|
+
context: "plugin.formatTools",
|
|
211
|
+
count: formattedTools.length
|
|
212
|
+
});
|
|
213
|
+
return formattedTools;
|
|
214
|
+
} catch (error) {
|
|
215
|
+
logger.error(
|
|
216
|
+
"Error formatting tools for Gab AI",
|
|
217
|
+
{ context: "plugin.formatTools" },
|
|
218
|
+
error instanceof Error ? error : void 0
|
|
219
|
+
);
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
/**
|
|
224
|
+
* Parse tool calls from Gab AI response format
|
|
225
|
+
* Extracts tool calls from Gab AI API responses (OpenAI format)
|
|
226
|
+
*
|
|
227
|
+
* @param response Gab AI API response object
|
|
228
|
+
* @returns Array of tool call requests
|
|
229
|
+
*/
|
|
230
|
+
parseToolCalls: (response) => {
|
|
231
|
+
logger.debug("Parsing tool calls from Gab AI response", {
|
|
232
|
+
context: "plugin.parseToolCalls"
|
|
233
|
+
});
|
|
234
|
+
try {
|
|
235
|
+
const toolCalls = (0, import_plugin_utils2.parseOpenAIToolCalls)(response);
|
|
236
|
+
logger.debug("Successfully parsed tool calls", {
|
|
237
|
+
context: "plugin.parseToolCalls",
|
|
238
|
+
count: toolCalls.length
|
|
239
|
+
});
|
|
240
|
+
return toolCalls;
|
|
241
|
+
} catch (error) {
|
|
242
|
+
logger.error(
|
|
243
|
+
"Error parsing tool calls from Gab AI response",
|
|
244
|
+
{ context: "plugin.parseToolCalls" },
|
|
245
|
+
error instanceof Error ? error : void 0
|
|
246
|
+
);
|
|
247
|
+
return [];
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
var index_default = plugin;
|
|
252
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
253
|
+
0 && (module.exports = {
|
|
254
|
+
plugin
|
|
255
|
+
});
|
package/index.ts
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gab AI Provider Plugin for Quilltap
|
|
3
|
+
* Main entry point that exports the plugin configuration
|
|
4
|
+
*
|
|
5
|
+
* This plugin provides:
|
|
6
|
+
* - Chat completion using Gab AI language models
|
|
7
|
+
* - Support for streaming responses
|
|
8
|
+
* - Text-only interactions (no file attachments)
|
|
9
|
+
*
|
|
10
|
+
* Implementation note: GabAIProvider extends OpenAICompatibleProvider,
|
|
11
|
+
* inheriting all the OpenAI-compatible logic with Gab-specific configuration.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { LLMProviderPlugin } from './types';
|
|
15
|
+
import { GabAIProvider } from './provider';
|
|
16
|
+
import { GabAIIcon } from './icon';
|
|
17
|
+
import {
|
|
18
|
+
createPluginLogger,
|
|
19
|
+
parseOpenAIToolCalls,
|
|
20
|
+
type OpenAIToolDefinition,
|
|
21
|
+
type ToolCallRequest,
|
|
22
|
+
} from '@quilltap/plugin-utils';
|
|
23
|
+
|
|
24
|
+
const logger = createPluginLogger('GabAI');
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Plugin metadata configuration
|
|
28
|
+
*/
|
|
29
|
+
const metadata = {
|
|
30
|
+
providerName: 'GAB_AI',
|
|
31
|
+
displayName: 'Gab AI',
|
|
32
|
+
description: 'Gab AI language models for chat completions',
|
|
33
|
+
colors: {
|
|
34
|
+
bg: 'bg-green-100',
|
|
35
|
+
text: 'text-green-800',
|
|
36
|
+
icon: 'text-green-600',
|
|
37
|
+
},
|
|
38
|
+
abbreviation: 'GAB',
|
|
39
|
+
} as const;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Configuration requirements
|
|
43
|
+
*/
|
|
44
|
+
const config = {
|
|
45
|
+
requiresApiKey: true,
|
|
46
|
+
requiresBaseUrl: false,
|
|
47
|
+
apiKeyLabel: 'Gab AI API Key',
|
|
48
|
+
} as const;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Supported capabilities
|
|
52
|
+
*/
|
|
53
|
+
const capabilities = {
|
|
54
|
+
chat: true,
|
|
55
|
+
imageGeneration: false,
|
|
56
|
+
embeddings: false,
|
|
57
|
+
webSearch: false,
|
|
58
|
+
} as const;
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* File attachment support
|
|
62
|
+
*/
|
|
63
|
+
const attachmentSupport = {
|
|
64
|
+
supportsAttachments: false as const,
|
|
65
|
+
supportedMimeTypes: [] as string[],
|
|
66
|
+
description: 'No attachment support (text only)',
|
|
67
|
+
notes: 'Gab AI does not currently support file attachments',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The Gab AI Provider Plugin
|
|
72
|
+
* Implements the LLMProviderPlugin interface for Quilltap
|
|
73
|
+
*
|
|
74
|
+
* Note: The underlying GabAIProvider extends OpenAICompatibleProvider,
|
|
75
|
+
* so all the core functionality is inherited from the openai-compatible plugin.
|
|
76
|
+
*/
|
|
77
|
+
export const plugin: LLMProviderPlugin = {
|
|
78
|
+
metadata,
|
|
79
|
+
|
|
80
|
+
config,
|
|
81
|
+
|
|
82
|
+
capabilities,
|
|
83
|
+
|
|
84
|
+
attachmentSupport,
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Factory method to create a Gab AI LLM provider instance
|
|
88
|
+
*/
|
|
89
|
+
createProvider: (baseUrl?: string) => {
|
|
90
|
+
logger.debug('Creating Gab AI provider instance', { context: 'plugin.createProvider', baseUrl });
|
|
91
|
+
return new GabAIProvider();
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get list of available models from Gab AI API
|
|
96
|
+
* Requires a valid API key
|
|
97
|
+
*/
|
|
98
|
+
getAvailableModels: async (apiKey: string, baseUrl?: string) => {
|
|
99
|
+
logger.debug('Fetching available Gab AI models', { context: 'plugin.getAvailableModels' });
|
|
100
|
+
try {
|
|
101
|
+
const provider = new GabAIProvider();
|
|
102
|
+
const models = await provider.getAvailableModels(apiKey);
|
|
103
|
+
logger.debug('Successfully fetched Gab AI models', { context: 'plugin.getAvailableModels', count: models.length });
|
|
104
|
+
return models;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
logger.error('Failed to fetch Gab AI models', { context: 'plugin.getAvailableModels' }, error instanceof Error ? error : undefined);
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Validate a Gab AI API key
|
|
113
|
+
*/
|
|
114
|
+
validateApiKey: async (apiKey: string, baseUrl?: string) => {
|
|
115
|
+
logger.debug('Validating Gab AI API key', { context: 'plugin.validateApiKey' });
|
|
116
|
+
try {
|
|
117
|
+
const provider = new GabAIProvider();
|
|
118
|
+
const isValid = await provider.validateApiKey(apiKey);
|
|
119
|
+
logger.debug('Gab AI API key validation result', { context: 'plugin.validateApiKey', isValid });
|
|
120
|
+
return isValid;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
logger.error('Error validating Gab AI API key', { context: 'plugin.validateApiKey' }, error instanceof Error ? error : undefined);
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get static model information
|
|
129
|
+
* Returns cached information about Gab AI models without needing API calls
|
|
130
|
+
*/
|
|
131
|
+
getModelInfo: () => {
|
|
132
|
+
logger.debug('Getting Gab AI model information', { context: 'plugin.getModelInfo' });
|
|
133
|
+
return [
|
|
134
|
+
{
|
|
135
|
+
id: 'gab-01',
|
|
136
|
+
name: 'Gab AI 01',
|
|
137
|
+
contextWindow: 128000,
|
|
138
|
+
maxOutputTokens: 4096,
|
|
139
|
+
supportsImages: false,
|
|
140
|
+
supportsTools: false,
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Render the Gab AI icon
|
|
147
|
+
*/
|
|
148
|
+
renderIcon: (props) => {
|
|
149
|
+
logger.debug('Rendering Gab AI icon', { context: 'plugin.renderIcon', className: props.className });
|
|
150
|
+
return GabAIIcon(props);
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Format tools from OpenAI format to OpenAI format
|
|
155
|
+
* Gab AI uses OpenAI format (inherited from OpenAI-compatible)
|
|
156
|
+
*
|
|
157
|
+
* @param tools Array of tools in OpenAI format
|
|
158
|
+
* @returns Array of tools in OpenAI format
|
|
159
|
+
*/
|
|
160
|
+
formatTools: (
|
|
161
|
+
tools: (OpenAIToolDefinition | Record<string, unknown>)[],
|
|
162
|
+
): OpenAIToolDefinition[] => {
|
|
163
|
+
logger.debug('Formatting tools for Gab AI provider', {
|
|
164
|
+
context: 'plugin.formatTools',
|
|
165
|
+
toolCount: tools.length,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const formattedTools: OpenAIToolDefinition[] = [];
|
|
170
|
+
|
|
171
|
+
for (const tool of tools) {
|
|
172
|
+
// Validate tool has function property (OpenAI format)
|
|
173
|
+
if (!('function' in tool)) {
|
|
174
|
+
logger.warn('Skipping tool with invalid format', {
|
|
175
|
+
context: 'plugin.formatTools',
|
|
176
|
+
});
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Tools already in OpenAI format, pass through
|
|
181
|
+
formattedTools.push(tool as OpenAIToolDefinition);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
logger.debug('Successfully formatted tools', {
|
|
185
|
+
context: 'plugin.formatTools',
|
|
186
|
+
count: formattedTools.length,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
return formattedTools;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
logger.error(
|
|
192
|
+
'Error formatting tools for Gab AI',
|
|
193
|
+
{ context: 'plugin.formatTools' },
|
|
194
|
+
error instanceof Error ? error : undefined
|
|
195
|
+
);
|
|
196
|
+
return [];
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Parse tool calls from Gab AI response format
|
|
202
|
+
* Extracts tool calls from Gab AI API responses (OpenAI format)
|
|
203
|
+
*
|
|
204
|
+
* @param response Gab AI API response object
|
|
205
|
+
* @returns Array of tool call requests
|
|
206
|
+
*/
|
|
207
|
+
parseToolCalls: (response: any): ToolCallRequest[] => {
|
|
208
|
+
logger.debug('Parsing tool calls from Gab AI response', {
|
|
209
|
+
context: 'plugin.parseToolCalls',
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
try {
|
|
213
|
+
const toolCalls = parseOpenAIToolCalls(response);
|
|
214
|
+
|
|
215
|
+
logger.debug('Successfully parsed tool calls', {
|
|
216
|
+
context: 'plugin.parseToolCalls',
|
|
217
|
+
count: toolCalls.length,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
return toolCalls;
|
|
221
|
+
} catch (error) {
|
|
222
|
+
logger.error(
|
|
223
|
+
'Error parsing tool calls from Gab AI response',
|
|
224
|
+
{ context: 'plugin.parseToolCalls' },
|
|
225
|
+
error instanceof Error ? error : undefined
|
|
226
|
+
);
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
export default plugin;
|
package/manifest.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../qtap-plugin-template/schemas/plugin-manifest.schema.json",
|
|
3
|
+
"name": "qtap-plugin-gab-ai",
|
|
4
|
+
"title": "Gab AI Provider",
|
|
5
|
+
"description": "Provides Gab AI language models for Quilltap",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Foundry-9 LLC",
|
|
9
|
+
"email": "charles.sebold@foundry-9.com",
|
|
10
|
+
"url": "https://foundry-9.com"
|
|
11
|
+
},
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"compatibility": {
|
|
14
|
+
"quilltapVersion": ">=1.7.0",
|
|
15
|
+
"nodeVersion": ">=18.0.0"
|
|
16
|
+
},
|
|
17
|
+
"capabilities": ["LLM_PROVIDER"],
|
|
18
|
+
"category": "PROVIDER",
|
|
19
|
+
"main": "index.js",
|
|
20
|
+
"typescript": true,
|
|
21
|
+
"frontend": "REACT",
|
|
22
|
+
"styling": "TAILWIND",
|
|
23
|
+
"enabledByDefault": true,
|
|
24
|
+
"status": "STABLE",
|
|
25
|
+
"keywords": ["gab", "gab-ai", "llm", "ai", "chat"],
|
|
26
|
+
"providerConfig": {
|
|
27
|
+
"providerName": "GAB_AI",
|
|
28
|
+
"displayName": "Gab AI",
|
|
29
|
+
"description": "Gab AI language models for chat completions",
|
|
30
|
+
"abbreviation": "GAB",
|
|
31
|
+
"colors": {
|
|
32
|
+
"bg": "bg-green-100",
|
|
33
|
+
"text": "text-green-800",
|
|
34
|
+
"icon": "text-green-600"
|
|
35
|
+
},
|
|
36
|
+
"requiresApiKey": true,
|
|
37
|
+
"requiresBaseUrl": false,
|
|
38
|
+
"apiKeyLabel": "Gab AI API Key",
|
|
39
|
+
"capabilities": {
|
|
40
|
+
"chat": true,
|
|
41
|
+
"imageGeneration": false,
|
|
42
|
+
"embeddings": false,
|
|
43
|
+
"webSearch": false
|
|
44
|
+
},
|
|
45
|
+
"attachmentSupport": {
|
|
46
|
+
"supported": false,
|
|
47
|
+
"mimeTypes": [],
|
|
48
|
+
"description": "No attachment support (text only)"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"permissions": {
|
|
52
|
+
"network": ["api.gab.com"],
|
|
53
|
+
"userData": false,
|
|
54
|
+
"database": false
|
|
55
|
+
}
|
|
56
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@quilltap/qtap-plugin-gab-ai",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Gab AI provider plugin for Quilltap",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"author": "Foundry-9 LLC",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "node esbuild.config.mjs",
|
|
11
|
+
"clean": "rm -f index.js"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@quilltap/plugin-utils": "^1.1.0"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"esbuild": "^0.24.0"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/provider.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gab AI Provider Implementation for Quilltap Plugin
|
|
3
|
+
*
|
|
4
|
+
* Gab AI is OpenAI-compatible and provides language models via api.gab.com/v1
|
|
5
|
+
* This provider extends OpenAICompatibleProvider from @quilltap/plugin-utils
|
|
6
|
+
* with static configuration specific to the Gab AI service.
|
|
7
|
+
*
|
|
8
|
+
* Note: Gab AI does not currently support file attachments or image generation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { OpenAICompatibleProvider } from '@quilltap/plugin-utils';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Gab AI Provider - extends OpenAICompatibleProvider with Gab-specific configuration
|
|
15
|
+
*
|
|
16
|
+
* Configuration:
|
|
17
|
+
* - baseUrl: https://gab.ai/v1 (fixed)
|
|
18
|
+
* - API key: required
|
|
19
|
+
* - Attachments: not supported
|
|
20
|
+
* - Image generation: not supported
|
|
21
|
+
*/
|
|
22
|
+
export class GabAIProvider extends OpenAICompatibleProvider {
|
|
23
|
+
constructor() {
|
|
24
|
+
super({
|
|
25
|
+
baseUrl: 'https://gab.ai/v1',
|
|
26
|
+
providerName: 'GabAI',
|
|
27
|
+
requireApiKey: true,
|
|
28
|
+
attachmentErrorMessage: 'Gab AI does not support file attachments',
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
package/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type exports for Gab AI Plugin
|
|
3
|
+
* Re-exports types from @quilltap/plugin-types for use within the plugin
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
FileAttachment,
|
|
8
|
+
ImageGenParams,
|
|
9
|
+
GeneratedImage,
|
|
10
|
+
ImageGenResponse,
|
|
11
|
+
LLMMessage,
|
|
12
|
+
LLMParams,
|
|
13
|
+
LLMResponse,
|
|
14
|
+
StreamChunk,
|
|
15
|
+
LLMProvider,
|
|
16
|
+
LLMProviderPlugin,
|
|
17
|
+
ProviderMetadata,
|
|
18
|
+
ProviderConfigRequirements,
|
|
19
|
+
ProviderCapabilities,
|
|
20
|
+
AttachmentSupport,
|
|
21
|
+
ModelInfo,
|
|
22
|
+
} from '@quilltap/plugin-types';
|