mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-09 22:38:10 -05:00
Compare commits
125 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a830394be | ||
|
|
9f8a2d3b59 | ||
|
|
4353bc9f7f | ||
|
|
7a8024ee79 | ||
|
|
b5bf75ad2e | ||
|
|
3fd923f6b8 | ||
|
|
eb251139b8 | ||
|
|
0b5d3cfc30 | ||
|
|
14a3c11930 | ||
|
|
c8cf6da0cc | ||
|
|
a2c954ba50 | ||
|
|
730d0adc86 | ||
|
|
dc9168ab6f | ||
|
|
e500a5916e | ||
|
|
6ddf46a379 | ||
|
|
e8aa358b15 | ||
|
|
62f373c2b4 | ||
|
|
fcf826f3de | ||
|
|
bd2db29cee | ||
|
|
c6d612ee9a | ||
|
|
d613c25974 | ||
|
|
c0abea7c66 | ||
|
|
496bd2812a | ||
|
|
70fccaf2fb | ||
|
|
9a71f7c96d | ||
|
|
5da3db383d | ||
|
|
19438cbd20 | ||
|
|
a0b71ee365 | ||
|
|
034513ece5 | ||
|
|
0affb9bab1 | ||
|
|
3305df8fb2 | ||
|
|
892c229076 | ||
|
|
599c5f2b9f | ||
|
|
19e5d8dbe0 | ||
|
|
b772127738 | ||
|
|
5dd61abe2a | ||
|
|
f45e140126 | ||
|
|
752a66cb48 | ||
|
|
da28d91d65 | ||
|
|
5a66ca1c5a | ||
|
|
98f3da610b | ||
|
|
73ce92ccd9 | ||
|
|
7f3f1d641f | ||
|
|
44b5c46beb | ||
|
|
8d37c9d6b9 | ||
|
|
1138d0b60e | ||
|
|
b78217088d | ||
|
|
76b889733d | ||
|
|
3911fd9f5d | ||
|
|
b06e29f8a8 | ||
|
|
11a7e542e1 | ||
|
|
6681078259 | ||
|
|
be1edf7b1d | ||
|
|
8ce748a1b1 | ||
|
|
96070f6f39 | ||
|
|
ca3e89a889 | ||
|
|
47d799d7ae | ||
|
|
4899ce56a5 | ||
|
|
4a7b7becec | ||
|
|
80fdccbe89 | ||
|
|
d9d8f7bf96 | ||
|
|
a96ddbeef0 | ||
|
|
d32a1d6a5a | ||
|
|
201474791d | ||
|
|
6d09137fee | ||
|
|
680febbe66 | ||
|
|
f59e5081f3 | ||
|
|
6a504c7422 | ||
|
|
89a0abcbe4 | ||
|
|
2dfd78ef0b | ||
|
|
2200b6ea08 | ||
|
|
82f9ebaf99 | ||
|
|
704ad3067a | ||
|
|
6f7e3c04d7 | ||
|
|
79f763456e | ||
|
|
9d4f7f1571 | ||
|
|
8e7373b308 | ||
|
|
7a39742507 | ||
|
|
cea218e61e | ||
|
|
02ac68834d | ||
|
|
f673f424da | ||
|
|
0ae41116aa | ||
|
|
2b11f3e48e | ||
|
|
ed77cc2320 | ||
|
|
29f19fce51 | ||
|
|
62ed5d2b9a | ||
|
|
836e4c4fab | ||
|
|
946c1af42d | ||
|
|
a74585cb14 | ||
|
|
5ffd458aa0 | ||
|
|
9786721037 | ||
|
|
ffb31985e8 | ||
|
|
eeee37a7cc | ||
|
|
bd89a8d776 | ||
|
|
2311e7e7a1 | ||
|
|
09b79283e9 | ||
|
|
7fbb5e0935 | ||
|
|
984d9d03f5 | ||
|
|
c47502fa8c | ||
|
|
1fe02bdf22 | ||
|
|
d550385a5e | ||
|
|
1e81da5f42 | ||
|
|
5b318dc402 | ||
|
|
4027305345 | ||
|
|
63879d5cf7 | ||
|
|
9539441496 | ||
|
|
352ade34c8 | ||
|
|
9abc69c1a9 | ||
|
|
93f6f2f0c4 | ||
|
|
1f5d3db3fb | ||
|
|
4446b456ba | ||
|
|
870941090a | ||
|
|
5fc004805e | ||
|
|
ce47018fc3 | ||
|
|
a09131ea72 | ||
|
|
36eb321059 | ||
|
|
47bf9600d6 | ||
|
|
be674841e7 | ||
|
|
39a8b67438 | ||
|
|
0a4950dd08 | ||
|
|
593c1558c0 | ||
|
|
c8f9a39a40 | ||
|
|
50ec02546f | ||
|
|
881085d0fe | ||
|
|
2d75052e57 |
@@ -11,6 +11,10 @@ on:
|
||||
permissions:
|
||||
contents: write # Ensure the workflow has write permissions
|
||||
|
||||
concurrency:
|
||||
group: version-update
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
update-version:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
@@ -30,6 +34,11 @@ jobs:
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Pull latest main and tags
|
||||
run: |
|
||||
git pull --rebase origin main
|
||||
git fetch --tags
|
||||
|
||||
- name: Get the latest tag
|
||||
id: get_latest_tag
|
||||
run: |
|
||||
|
||||
65
README.md
65
README.md
@@ -13,15 +13,17 @@ Fabric is graciously supported by…
|
||||

|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
<div align="center">
|
||||
<p class="align center">
|
||||
<h4><code>fabric</code> is an open-source framework for augmenting humans using AI.</h4>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
[Updates](#updates) •
|
||||
[What and Why](#what-and-why) •
|
||||
[Philosophy](#philosophy) •
|
||||
[Installation](#Installation) •
|
||||
[Usage](#Usage) •
|
||||
[Installation](#installation) •
|
||||
[Usage](#usage) •
|
||||
[Examples](#examples) •
|
||||
[Just Use the Patterns](#just-use-the-patterns) •
|
||||
[Custom Patterns](#custom-patterns) •
|
||||
@@ -32,6 +34,30 @@ Fabric is graciously supported by…
|
||||
|
||||
</div>
|
||||
|
||||
## What and why
|
||||
|
||||
Since the start of modern AI in late 2022 we've seen an **_extraordinary_** number of AI applications for accomplishing tasks. There are thousands of websites, chatbots, mobile apps, and other interfaces for using all the differnet AI out there.
|
||||
|
||||
It's all really exciting and powerful, but _it's not easy to integrate this functionality into our lives._
|
||||
|
||||
<p class="align center">
|
||||
<h4>In other words, AI doesn't have a capabilities problem—it has an <em>integration</em> problem.</h4>
|
||||
</p>
|
||||
|
||||
**Fabric was created to address this by creating and organizating the fundamental units of AI—the prompts themselves!**
|
||||
|
||||
Fabric organizes prompts by real-world task, allowing people to create, collect, and organize their most important AI solutions in a single place for use in their favorite tools. And if you're command-line focused, you can use Fabric itself as the interface!
|
||||
|
||||
## Intro videos
|
||||
|
||||
Keep in mind that many of these were recorded when Fabric was Python-based, so remember to use the current [install instructions](#installation) below.
|
||||
|
||||
- [Network Chuck](https://www.youtube.com/watch?v=UbDyjIIGaxQ)
|
||||
- [David Bombal](https://www.youtube.com/watch?v=vF-MQmVxnCs)
|
||||
- [My Own Intro to the Tool](https://www.youtube.com/watch?v=wPEyyigh10g)
|
||||
- [More Fabric YouTube Videos](https://www.youtube.com/results?search_query=fabric+ai)
|
||||
|
||||
|
||||
## Navigation
|
||||
|
||||
- [`fabric`](#fabric)
|
||||
@@ -87,28 +113,21 @@ Fabric is graciously supported by…
|
||||
## Updates
|
||||
|
||||
> [!NOTE]
|
||||
> April 16, 2025
|
||||
>
|
||||
> - Fabric now supports Grok (from XAI)! Update and use `-S` to select it as your default if you want, or just use the shortcut `-m grok-3-beta`. Enjoy!
|
||||
|
||||
## What and why
|
||||
|
||||
Since the start of 2023 and GenAI we've seen a massive number of AI applications for accomplishing tasks. It's powerful, but _it's not easy to integrate this functionality into our lives._
|
||||
|
||||
<div align="center">
|
||||
<h4>In other words, AI doesn't have a capabilities problem—it has an <em>integration</em> problem.</h4>
|
||||
</div>
|
||||
|
||||
Fabric was created to address this by enabling everyone to granularly apply AI to everyday challenges.
|
||||
|
||||
## Intro videos
|
||||
|
||||
Keep in mind that many of these were recorded when Fabric was Python-based, so remember to use the current [install instructions](#installation) below.
|
||||
|
||||
- [Network Chuck](https://www.youtube.com/watch?v=UbDyjIIGaxQ)
|
||||
- [David Bombal](https://www.youtube.com/watch?v=vF-MQmVxnCs)
|
||||
- [My Own Intro to the Tool](https://www.youtube.com/watch?v=wPEyyigh10g)
|
||||
- [More Fabric YouTube Videos](https://www.youtube.com/results?search_query=fabric+ai)
|
||||
>June 17, 2025
|
||||
>
|
||||
>- Fabric now supports Perplexity AI. Configure it by using `fabric -S` to add your Perlexity AI API Key,
|
||||
> and then try:
|
||||
>
|
||||
> ```bash
|
||||
> fabric -m sonar-pro "What is the latest world news?"
|
||||
> ```
|
||||
>
|
||||
>June 11, 2025
|
||||
>
|
||||
>- Fabric's YouTube transcription now needs `yt-dlp` to be installed. Make sure to install the latest
|
||||
> version (2025.06.09 as of this note). The YouTube API key is only needed for comments (the `--comments` flag)
|
||||
> and metadata extraction (the `--metadata` flag).
|
||||
|
||||
## Philosophy
|
||||
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
This Cumulative PR adds several Web UI and functionality improvements to make pattern selection more intuitive with the addition of pattern descriptions, ability to save favorite patterns, a Pattern TAG system, powerful multilingual capabilities, PDF-to-markdown functionalities, a help reference section, more robust Youtube processing and a variety of other ui improvements.
|
||||
|
||||
## 🎥 Demo Video
|
||||
https://youtu.be/XMzjgqvdltM
|
||||
|
||||
|
||||
|
||||
## 🌟 Key Features
|
||||
|
||||
### 1. Web UI and Pattern Selection Improvements
|
||||
- Pattern Descriptions
|
||||
- Pattern Tags
|
||||
- Pattern Favourites
|
||||
- Pattern Search bar
|
||||
- PDF to markdown (pdf as pattern input)
|
||||
- Better handling of Youtube url
|
||||
- Multilingual Support
|
||||
- Web UI refinements for clearer interaction
|
||||
- Help section via modal
|
||||
|
||||
### 2. Multilingual Support System
|
||||
- Seamless language switching via UI dropdown
|
||||
- Persistent language state management
|
||||
- Pattern processing now use the selected language seamlessly
|
||||
|
||||
### 3. YouTube Integration Enhancement
|
||||
- Robust language handling for YouTube transcript processing
|
||||
- Chunk-based language maintenance for long transcripts
|
||||
- Consistent language output throughout transcript analysis
|
||||
|
||||
### 4. Enhanced Tag Management Integration
|
||||
|
||||
The tag filtering system has been deeply integrated into the Pattern Selection interface through several UI enhancements:
|
||||
|
||||
1. **Dual-Position Tag Panel**
|
||||
- Sliding panel positioned to the right of pattern modal
|
||||
- Dynamic toggle button that adapts position and text based on panel state
|
||||
- Smooth transitions for opening/closing animations
|
||||
|
||||
2. **Tag Selection Visibility**
|
||||
- New dedicated tag display section in pattern modal
|
||||
- Visual separation through subtle background styling
|
||||
- Immediate feedback showing selected tags with comma separation
|
||||
- Inline reset capability for quick tag clearing
|
||||
|
||||
3. **Improved User Experience**
|
||||
- Clear visual hierarchy between pattern list and tag filtering
|
||||
- Multiple ways to manage tags (panel or quick reset)
|
||||
- Consistent styling with existing design language
|
||||
- Space-efficient tag brick layout in 3-column grid
|
||||
|
||||
4. **Technical Implementation**
|
||||
- Reactive tag state management
|
||||
- Efficient tag filtering logic
|
||||
- Proper event dispatching between components
|
||||
- Maintained accessibility standards
|
||||
- Responsive design considerations
|
||||
|
||||
|
||||
5. **PDF to Markdown conversion functionality for the web interface**
|
||||
- Automatic detection and processing of PDF files in chat
|
||||
- Conversion to markdown format for LLM processing
|
||||
- Installation instructions from the pdf-to-markdown repository
|
||||
|
||||
The PDF conversion module has been integrated in the svelte web browser interface. Once installed, it will automatically detect pdf files in the chat interface and convert them to markdown
|
||||
|
||||
|
||||
## HOW TO INSTALL PDF-TO-MARKDOWN
|
||||
If you need to update the web component follow the instructions in "Web Interface MOD Readme Files/WEB V2 Install Guide.md".
|
||||
|
||||
Assuming your web install is up to date and web svelte config complete, you can simply follow these steps to add Pdf-to-mardown.
|
||||
|
||||
# FROM FABRIC ROOT DIRECTORY
|
||||
cd .. web
|
||||
|
||||
# Install in this sequence:
|
||||
# Step 1
|
||||
npm install -D patch-package
|
||||
# Step 2
|
||||
npm install -D pdfjs-dist@2.5.207
|
||||
# Step 3
|
||||
npm install -D github:jzillmann/pdf-to-markdown#modularize
|
||||
|
||||
These enhancements create a more intuitive and efficient pattern discovery experience, allowing users to quickly filter and find relevant patterns while maintaining a clean, modern interface.
|
||||
|
||||
|
||||
## 🛠 Technical Implementation
|
||||
|
||||
### Language Support Architecture
|
||||
```typescript
|
||||
// Language state management
|
||||
export const languageStore = writable<string>('');
|
||||
|
||||
// Chat input language detection
|
||||
if (qualifier === 'fr') {
|
||||
languageStore.set('fr');
|
||||
userInput = userInput.replace(/--fr\s*/, '');
|
||||
}
|
||||
|
||||
// Service layer integration
|
||||
const language = get(languageStore) || 'en';
|
||||
const languageInstruction = language !== 'en'
|
||||
? `. Please use the language '${language}' for the output.`
|
||||
: '';
|
||||
```
|
||||
|
||||
### YouTube Processing Enhancement
|
||||
```typescript
|
||||
// Process stream with language instruction per chunk
|
||||
await chatService.processStream(
|
||||
stream,
|
||||
(content: string, response?: StreamResponse) => {
|
||||
if (currentLanguage !== 'en') {
|
||||
content = `${content}. Please use the language '${currentLanguage}' for the output.`;
|
||||
}
|
||||
// Update messages...
|
||||
}
|
||||
);
|
||||
```
|
||||
# Pattern Descriptions and Tags Management
|
||||
|
||||
This document explains the complete workflow for managing pattern descriptions and tags, including how to process new patterns and maintain metadata.
|
||||
|
||||
## System Overview
|
||||
|
||||
The pattern system follows this hierarchy:
|
||||
1. `~/.config/fabric/patterns/` directory: The source of truth for available patterns
|
||||
2. `pattern_extracts.json`: Contains first 500 words of each pattern for reference
|
||||
3. `pattern_descriptions.json`: Stores pattern metadata (descriptions and tags)
|
||||
4. `web/static/data/pattern_descriptions.json`: Web-accessible copy for the interface
|
||||
|
||||
## Pattern Processing Workflow
|
||||
|
||||
### 1. Adding New Patterns
|
||||
- Add patterns to `~/.config/fabric/patterns/`
|
||||
- Run extract_patterns.py to process new additions:
|
||||
```bash
|
||||
python extract_patterns.py
|
||||
|
||||
The Python Script automatically:
|
||||
- Creates pattern extracts for reference
|
||||
- Adds placeholder entries in descriptions file
|
||||
- Syncs to web interface
|
||||
|
||||
### 2. Pattern Extract Creation
|
||||
The script extracts first 500 words from each pattern's system.md file to:
|
||||
|
||||
- Provide context for writing descriptions
|
||||
- Maintain reference material
|
||||
- Aid in pattern categorization
|
||||
|
||||
### 3. Description and Tag Management
|
||||
Pattern descriptions and tags are managed in pattern_descriptions.json:
|
||||
|
||||
|
||||
{
|
||||
"patterns": [
|
||||
{
|
||||
"patternName": "pattern_name",
|
||||
"description": "[Description pending]",
|
||||
"tags": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
## Completing Pattern Metadata
|
||||
|
||||
### Writing Descriptions
|
||||
1. Check pattern_descriptions.json for "[Description pending]" entries
|
||||
2. Reference pattern_extracts.json for context
|
||||
|
||||
3. How to update Pattern short descriptions (one sentence).
|
||||
|
||||
You can update your descriptions in pattern_descriptions.json manually or using LLM assistance (prefered approach).
|
||||
|
||||
Tell AI to look for "Description pending" entries in this file and write a short description based on the extract info in the pattern_extracts.json file. You can also ask your LLM to add tags for those newly added patterns, using other patterns tag assignments as example.
|
||||
|
||||
### Managing Tags
|
||||
1. Add appropriate tags to new patterns
|
||||
2. Update existing tags as needed
|
||||
3. Tags are stored as arrays: ["TAG1", "TAG2"]
|
||||
4. Edit pattern_descriptions.json directly to modify tags
|
||||
5. Make tags your own. You can delete, replace, amend existing tags.
|
||||
|
||||
## File Synchronization
|
||||
|
||||
The script maintains synchronization between:
|
||||
- Local pattern_descriptions.json
|
||||
- Web interface copy in static/data/
|
||||
- No manual file copying needed
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. Run extract_patterns.py when:
|
||||
- Adding new patterns
|
||||
- Updating existing patterns
|
||||
- Modifying pattern structure
|
||||
|
||||
2. Description Writing:
|
||||
- Use pattern extracts for context
|
||||
- Keep descriptions clear and concise
|
||||
- Focus on pattern purpose and usage
|
||||
|
||||
3. Tag Management:
|
||||
- Use consistent tag categories
|
||||
- Apply multiple tags when relevant
|
||||
- Update tags to reflect pattern evolution
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
If patterns are not showing in the web interface:
|
||||
1. Verify pattern_descriptions.json format
|
||||
2. Check web static copy exists
|
||||
3. Ensure proper file permissions
|
||||
4. Run extract_patterns.py to resync
|
||||
|
||||
## File Structure
|
||||
|
||||
fabric/
|
||||
├── patterns/ # Pattern source files
|
||||
├── PATTERN_DESCRIPTIONS/
|
||||
│ ├── extract_patterns.py # Pattern processing script
|
||||
│ ├── pattern_extracts.json # Pattern content references
|
||||
│ └── pattern_descriptions.json # Pattern metadata
|
||||
└── web/
|
||||
└── static/
|
||||
└── data/
|
||||
└── pattern_descriptions.json # Web interface copy
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## 🎯 Usage Examples
|
||||
|
||||
### 1. Using Language Qualifiers
|
||||
```
|
||||
User: What is the weather?
|
||||
AI: The weather information...
|
||||
|
||||
User: --fr What is the weather?
|
||||
AI: Voici les informations météo...
|
||||
```
|
||||
|
||||
### 2. Global Settings
|
||||
1. Select language from dropdown
|
||||
2. All interactions use selected language
|
||||
3. Automatic reset to English after each message
|
||||
|
||||
### 3. YouTube Analysis
|
||||
```
|
||||
User: Analyze this YouTube video --fr
|
||||
AI: [Provides analysis in French, maintaining language throughout the transcript]
|
||||
```
|
||||
|
||||
## 💡 Key Benefits
|
||||
|
||||
1. **Enhanced User Experience**
|
||||
- Intuitive language switching
|
||||
- Consistent language handling
|
||||
- Seamless integration with existing features
|
||||
|
||||
2. **Robust Implementation**
|
||||
- Simple yet powerful design
|
||||
- No complex language detection needed
|
||||
- Direct AI instruction approach
|
||||
|
||||
3. **Maintainable Architecture**
|
||||
- Clean separation of concerns
|
||||
- Stateful language management
|
||||
- Easy to extend for new languages
|
||||
|
||||
4. **YouTube Integration**
|
||||
- Handles long transcripts effectively
|
||||
- Maintains language consistency
|
||||
- Robust chunk processing
|
||||
|
||||
## 🔄 Implementation Notes
|
||||
|
||||
1. **State Management**
|
||||
- Language persists until changed
|
||||
- Resets to English after each message
|
||||
- Handles UI state updates efficiently
|
||||
|
||||
2. **Error Handling**
|
||||
- Invalid qualifiers are ignored
|
||||
- Unknown languages default to English
|
||||
- Proper store reset on errors
|
||||
|
||||
3. **Best Practices**
|
||||
- Clear language instructions
|
||||
- Consistent state management
|
||||
- Robust error handling
|
||||
|
||||
@@ -256,7 +256,8 @@ func Cli(version string) (err error) {
|
||||
}
|
||||
|
||||
var chatter *core.Chatter
|
||||
if chatter, err = registry.GetChatter(currentFlags.Model, currentFlags.ModelContextLength, currentFlags.Strategy, currentFlags.Stream, currentFlags.DryRun); err != nil {
|
||||
if chatter, err = registry.GetChatter(currentFlags.Model, currentFlags.ModelContextLength,
|
||||
currentFlags.Strategy, currentFlags.Stream, currentFlags.DryRun); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ type ChatOptions struct {
|
||||
Raw bool
|
||||
Seed int
|
||||
ModelContextLength int
|
||||
MaxTokens int
|
||||
}
|
||||
|
||||
// NormalizeMessages remove empty messages and ensure messages order user-assist-user
|
||||
|
||||
@@ -32,6 +32,13 @@ type Chatter struct {
|
||||
|
||||
// Send processes a chat request and applies any file changes if using the create_coding_feature pattern
|
||||
func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (session *fsdb.Session, err error) {
|
||||
modelToUse := opts.Model
|
||||
if modelToUse == "" {
|
||||
modelToUse = o.model // Default to the model set in the Chatter struct
|
||||
}
|
||||
if o.vendor.NeedsRawMode(modelToUse) {
|
||||
opts.Raw = true
|
||||
}
|
||||
if session, err = o.BuildSession(request, opts.Raw); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -192,18 +199,22 @@ func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *
|
||||
}
|
||||
|
||||
if raw {
|
||||
// In raw mode, systemMessage is prepended to request.Message.Content
|
||||
// and request.Message.Role is set to User.
|
||||
// In raw mode, we want to avoid duplicating the input that's already in the pattern
|
||||
var finalContent string
|
||||
if systemMessage != "" {
|
||||
if request.Message != nil {
|
||||
request.Message.Content = fmt.Sprintf("%s\\n\\n%s", systemMessage, request.Message.Content)
|
||||
request.Message.Role = goopenai.ChatMessageRoleUser
|
||||
// If we have a pattern, it already includes the user input
|
||||
if request.PatternName != "" {
|
||||
finalContent = systemMessage
|
||||
} else {
|
||||
// If no user message originally, create one with the system content.
|
||||
request.Message = &goopenai.ChatCompletionMessage{Role: goopenai.ChatMessageRoleUser, Content: systemMessage}
|
||||
// No pattern, combine system message with user input
|
||||
finalContent = fmt.Sprintf("%s\n\n%s", systemMessage, request.Message.Content)
|
||||
}
|
||||
request.Message = &goopenai.ChatCompletionMessage{
|
||||
Role: goopenai.ChatMessageRoleUser,
|
||||
Content: finalContent,
|
||||
}
|
||||
}
|
||||
// After this, if request.Message is not nil, it's the (potentially combined) message to be appended.
|
||||
// After this, if request.Message is not nil, append it
|
||||
if request.Message != nil {
|
||||
session.Append(request.Message)
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/plugins/ai/bedrock"
|
||||
"github.com/danielmiessler/fabric/plugins/ai/exolab"
|
||||
"github.com/danielmiessler/fabric/plugins/ai/perplexity" // Added Perplexity plugin
|
||||
"github.com/danielmiessler/fabric/plugins/strategy"
|
||||
|
||||
"github.com/samber/lo"
|
||||
@@ -34,6 +36,33 @@ import (
|
||||
"github.com/danielmiessler/fabric/plugins/tools/youtube"
|
||||
)
|
||||
|
||||
// hasAWSCredentials checks if any AWS credentials are present either in the
|
||||
// environment variables or in the default/shared credentials file. It doesn't
|
||||
// attempt to verify the validity of the credentials, but simply ensures that a
|
||||
// potential authentication source exists so we can safely initialize the
|
||||
// Bedrock client without causing the AWS SDK to search for credentials.
|
||||
func hasAWSCredentials() bool {
|
||||
if os.Getenv("AWS_PROFILE") != "" ||
|
||||
os.Getenv("AWS_ROLE_SESSION_NAME") != "" ||
|
||||
(os.Getenv("AWS_ACCESS_KEY_ID") != "" && os.Getenv("AWS_SECRET_ACCESS_KEY") != "") {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
credFile := os.Getenv("AWS_SHARED_CREDENTIALS_FILE")
|
||||
if credFile == "" {
|
||||
if home, err := os.UserHomeDir(); err == nil {
|
||||
credFile = filepath.Join(home, ".aws", "credentials")
|
||||
}
|
||||
}
|
||||
if credFile != "" {
|
||||
if _, err := os.Stat(credFile); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
|
||||
ret = &PluginRegistry{
|
||||
Db: db,
|
||||
@@ -66,8 +95,13 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
|
||||
anthropic.NewClient(),
|
||||
lmstudio.NewClient(),
|
||||
exolab.NewClient(),
|
||||
perplexity.NewClient(), // Added Perplexity client
|
||||
)
|
||||
|
||||
if hasAWSCredentials() {
|
||||
vendors = append(vendors, bedrock.NewClient())
|
||||
}
|
||||
|
||||
// Add all OpenAI-compatible providers
|
||||
for providerName := range openai_compatible.ProviderMap {
|
||||
provider, _ := openai_compatible.GetProviderByName(providerName)
|
||||
|
||||
87
go.mod
87
go.mod
@@ -6,40 +6,56 @@ toolchain go1.24.2
|
||||
|
||||
require (
|
||||
github.com/anaskhan96/soup v1.2.5
|
||||
github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3
|
||||
github.com/anthropics/anthropic-sdk-go v1.4.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.9
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-git/go-git/v5 v5.16.0
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-shiori/go-readability v0.0.0-20250217085726-9f5bf5ca7612
|
||||
github.com/google/generative-ai-go v0.19.0
|
||||
github.com/google/generative-ai-go v0.20.1
|
||||
github.com/jessevdk/go-flags v1.6.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/ollama/ollama v0.6.6
|
||||
github.com/ollama/ollama v0.9.0
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/samber/lo v1.49.1
|
||||
github.com/sashabaranov/go-openai v1.38.2
|
||||
github.com/samber/lo v1.50.0
|
||||
github.com/sashabaranov/go-openai v1.40.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/text v0.24.0
|
||||
google.golang.org/api v0.230.0
|
||||
golang.org/x/text v0.26.0
|
||||
google.golang.org/api v0.236.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.120.1 // indirect
|
||||
cloud.google.com/go/ai v0.10.2 // indirect
|
||||
cloud.google.com/go/auth v0.16.1 // indirect
|
||||
cloud.google.com/go v0.121.2 // indirect
|
||||
cloud.google.com/go/ai v0.12.1 // indirect
|
||||
cloud.google.com/go/auth v0.16.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.6.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.7.0 // indirect
|
||||
cloud.google.com/go/longrunning v0.6.7 // indirect
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.2.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.34.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 // indirect
|
||||
github.com/aws/smithy-go v1.22.2 // indirect
|
||||
github.com/bytedance/sonic v1.13.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
@@ -50,7 +66,7 @@ require (
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
@@ -62,7 +78,7 @@ require (
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
@@ -75,31 +91,32 @@ require (
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/sgaunet/perplexity-go/v2 v2.8.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/ugorji/go/codec v1.2.14 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
golang.org/x/arch v0.16.0 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.29.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f // indirect
|
||||
google.golang.org/grpc v1.72.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
golang.org/x/arch v0.18.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
|
||||
google.golang.org/grpc v1.73.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
||||
194
go.sum
194
go.sum
@@ -1,38 +1,76 @@
|
||||
cloud.google.com/go v0.120.1 h1:Z+5V7yd383+9617XDCyszmK5E4wJRJL+tquMfDj9hLM=
|
||||
cloud.google.com/go v0.120.1/go.mod h1:56Vs7sf/i2jYM6ZL9NYlC82r04PThNcPS5YgFmb0rp8=
|
||||
cloud.google.com/go/ai v0.10.2 h1:5NHzmZlRs+3kvlsVdjT0cTnLrjQdROJ/8VOljVfs+8o=
|
||||
cloud.google.com/go/ai v0.10.2/go.mod h1:xZuZuE9d3RgsR132meCnPadiU9XV0qXjpLr+P4J46eE=
|
||||
cloud.google.com/go/auth v0.16.1 h1:XrXauHMd30LhQYVRHLGvJiYeczweKQXZxsTbV9TiguU=
|
||||
cloud.google.com/go/auth v0.16.1/go.mod h1:1howDHJ5IETh/LwYs3ZxvlkXF48aSqqJUM+5o02dNOI=
|
||||
cloud.google.com/go v0.121.2 h1:v2qQpN6Dx9x2NmwrqlesOt3Ys4ol5/lFZ6Mg1B7OJCg=
|
||||
cloud.google.com/go v0.121.2/go.mod h1:nRFlrHq39MNVWu+zESP2PosMWA0ryJw8KUBZ2iZpxbw=
|
||||
cloud.google.com/go/ai v0.12.1 h1:m1n/VjUuHS+pEO/2R4/VbuuEIkgk0w67fDQvFaMngM0=
|
||||
cloud.google.com/go/ai v0.12.1/go.mod h1:5vIPNe1ZQsVZqCliXIPL4QnhObQQY4d9hAGHdVc4iw4=
|
||||
cloud.google.com/go/auth v0.16.2 h1:QvBAGFPLrDeoiNjyfVunhQ10HKNYuOwZ5noee0M5df4=
|
||||
cloud.google.com/go/auth v0.16.2/go.mod h1:sRBas2Y1fB1vZTdurouM0AzuYQBMZinrUYL8EufhtEA=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||
cloud.google.com/go/compute/metadata v0.7.0 h1:PBWF+iiAerVNe8UCHxdOt6eHLVc3ydFeOCw78U8ytSU=
|
||||
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||
cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE=
|
||||
cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY=
|
||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs=
|
||||
github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/anaskhan96/soup v1.2.5 h1:V/FHiusdTrPrdF4iA1YkVxsOpdNcgvqT1hG+YtcZ5hM=
|
||||
github.com/anaskhan96/soup v1.2.5/go.mod h1:6YnEp9A2yywlYdM4EgDz9NEHclocMepEtku7wg6Cq3s=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3 h1:b5t1ZJMvV/l99y4jbz7kRFdUp3BSDkI8EhSlHczivtw=
|
||||
github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
|
||||
github.com/anthropics/anthropic-sdk-go v1.4.0 h1:fU1jKxYbQdQDiEXCxeW5XZRIOwKevn/PMg8Ay1nnUx0=
|
||||
github.com/anthropics/anthropic-sdk-go v1.4.0/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4 h1:GySzjhVvx0ERP6eyfAbAuAXLtAda5TEy19E5q5W8I9E=
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.4/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 h1:zAybnyUQXIZ5mok5Jqwlf58/TFE7uvd3IAsa1aF9cXs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10/go.mod h1:qqvMj6gHLR/EXWZw4ZbqlPbQUyenf4h82UQUlKc+l14=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27 h1:HdqgGt1OAP0HkEDDShEl0oSYa9ZZBSOmKpdpsDMdO90=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.27/go.mod h1:MVYamCg76dFNINkZFu4n4RjDixhVr51HLj4ErWzrVwg=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27 h1:2raNba6gr2IfA0eqqiP2XiQ0UVOpGPgDSi0I9iAP+UI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.27/go.mod h1:gniiwbGahQByxan6YjQUMcW4Aov6bLC3m+evgcoN4r4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11 h1:KreluoV8FZDEtI6Co2xuNk/UqI9iwMrOx/87PBNIKqw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.11/go.mod h1:SeSUYBLsMYFoRvHE0Tjvn7kbxaUhl75CJi1sbfhMxkU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35 h1:o1v1VFfPcDVlK3ll1L5xHsaQAFdNtZ5GXnNR7SwueC4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.35/go.mod h1:rZUQNYMNG+8uZxz9FOerQJ+FceCiodXvixpeRtdESrU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35 h1:R5b82ubO2NntENm3SAm0ADME+H630HomNJdgv+yZ3xw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.35/go.mod h1:FuA+nmgMRfkzVKYDNEqQadvEMxtxl9+RLT9ribCwEMs=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.34.1 h1:sD4KqDKG8aOaMWaWTMB8l8VnLa/Di7XHb0Uf4plrndA=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.34.1/go.mod h1:lrn8DOVFYFeaUZKxJ95T5eGDBjnhffgGz68Wq2sfBbA=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0 h1:eMOwQ8ZZK+76+08RfxeaGUtRFN6wxmD1rvqovc2kq2w=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.30.0/go.mod h1:0b5Rq7rUvSQFYHI1UO0zFTV/S6j6DUyuykXA80C+YOI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17 h1:HGErhhrxZlQ044RiM+WdoZxp0p+EGM62y3L6pwA4olE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.17/go.mod h1:RkZEx4l0EHYDJpWppMJ3nD9wZJAa8/0lq9aVC+r2UII=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4 h1:BXx0ZIxvrJdSgSvKTZ+yRBeSqqgPM89VPlulEcl37tM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.22.4/go.mod h1:ooyCOXjvJEsUw7x+ZDHeISPMhtwI3ZCB7ggFMcFfWLU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4 h1:yiwVzJW2ZxZTurVbYWA7QOrAaCYQR72t0wrSBfoesUE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.4/go.mod h1:0oxfLkpz3rQ/CHlx5hB7H69YUpFiI1tql6Q6Ne+1bCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3 h1:ZsDKRLXGWHk8WdtyYMoGNO7bTudrvuKpDKgMVRlepGE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.30.3/go.mod h1:zwySh8fpFyXp9yOr/KVzxOl8SRqgf/IDw5aUt9UKFcQ=
|
||||
github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ=
|
||||
github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
|
||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
@@ -56,8 +94,8 @@ github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBv
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
@@ -66,11 +104,11 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
|
||||
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@@ -93,8 +131,8 @@ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8J
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/google/generative-ai-go v0.19.0 h1:R71szggh8wHMCUlEMsW2A/3T+5LdEIkiaHSYgSpUgdg=
|
||||
github.com/google/generative-ai-go v0.19.0/go.mod h1:JYolL13VG7j79kM5BtHz4qwONHkeJQzOCkKXnpqtS/E=
|
||||
github.com/google/generative-ai-go v0.20.1 h1:6dEIujpgN2V0PgLhr6c/M1ynRdc7ARtiIDPFzj45uNQ=
|
||||
github.com/google/generative-ai-go v0.20.1/go.mod h1:TjOnZJmZKzarWbjUJgy+r3Ee7HGBRVLhOIgupnwR4Bg=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
@@ -105,8 +143,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
|
||||
github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0=
|
||||
github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||
@@ -138,8 +176,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/ollama/ollama v0.6.6 h1:rnCQTSTiRD3Dsvd35dh2j2YB9DlQMFQR/y3XOhWZOmI=
|
||||
github.com/ollama/ollama v0.6.6/go.mod h1:pGgtoNyc9DdM6oZI6yMfI6jTk2Eh4c36c2GpfQCH7PY=
|
||||
github.com/ollama/ollama v0.9.0 h1:GvdGhi8G/QMnFrY0TMLDy1bXua+Ify8KTkFe4ZY/OZs=
|
||||
github.com/ollama/ollama v0.9.0/go.mod h1:aio9yQ7nc4uwIbn6S0LkGEPgn8/9bNQLL1nHuH+OcD0=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
||||
@@ -157,13 +195,15 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/sashabaranov/go-openai v1.38.2 h1:akrssjj+6DY3lWuDwHv6cBvJ8Z+FZDM9XEaaYFt0Auo=
|
||||
github.com/sashabaranov/go-openai v1.38.2/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||
github.com/sashabaranov/go-openai v1.40.1 h1:bJ08Iwct5mHBVkuvG6FEcb9MDTfsXdTYPGjYLRdeTEU=
|
||||
github.com/sashabaranov/go-openai v1.40.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adOgerWeFyZLUhAKRg=
|
||||
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.8.0 h1:stnuVieniZMGo6qJLCV2JyR2uF7K5398YOA/ZZcgrSg=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.8.0/go.mod h1:MSks4RNuivCi0GqJyylhFdgSJFVEwZHjAhrf86Wkynk=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
@@ -192,29 +232,29 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.2.14 h1:yOQvXCBc3Ij46LRkRoh4Yd5qK6LVOgi0bYOXfb7ifjw=
|
||||
github.com/ugorji/go/codec v1.2.14/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
||||
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
|
||||
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
|
||||
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
|
||||
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
|
||||
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4=
|
||||
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
|
||||
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
|
||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
@@ -222,8 +262,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@@ -242,10 +282,10 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -253,8 +293,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -271,8 +311,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -282,8 +322,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -294,10 +334,10 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
@@ -305,14 +345,16 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.230.0 h1:2u1hni3E+UXAXrONrrkfWpi/V6cyKVAbfGVeGtC3OxM=
|
||||
google.golang.org/api v0.230.0/go.mod h1:aqvtoMk7YkiXx+6U12arQFExiRV9D/ekvMCwCd/TksQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f h1:tjZsroqekhC63+WMqzmWyW5Twj/ZfR5HAlpd5YQ1Vs0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:Cd8IzgPo5Akum2c9R6FsXNaZbH3Jpa2gpHlW89FqlyQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f h1:N/PrbTw4kdkqNRzVfWPrBekzLuarFREcbFOiOLkXon4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250422160041-2d3770c4ea7f/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/api v0.236.0 h1:CAiEiDVtO4D/Qja2IA9VzlFrgPnK3XVMmRoJZlSWbc0=
|
||||
google.golang.org/api v0.236.0/go.mod h1:X1WF9CU2oTc+Jml1tiIxGmWFK/UZezdqEu09gcxZAj4=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78=
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -2,32 +2,32 @@ schema = 3
|
||||
|
||||
[mod]
|
||||
[mod."cloud.google.com/go"]
|
||||
version = "v0.120.1"
|
||||
hash = "sha256-yWaLc06rGXk16K53rix8O4uPSX+AOZDgIpIXf+wlh10="
|
||||
version = "v0.121.2"
|
||||
hash = "sha256-BCgGHxKti8slH98UDDurtgzX3lgcYEklsmj4ImPpwlc="
|
||||
[mod."cloud.google.com/go/ai"]
|
||||
version = "v0.10.2"
|
||||
hash = "sha256-bsqvdylG8kk+AHtyvMRMv1TOjUmvONAgJ+14mKcwuzs="
|
||||
version = "v0.12.1"
|
||||
hash = "sha256-wg3oLMS68E/v7EdNzywbjwEmpk+u6U8LTnIc1pq8edo="
|
||||
[mod."cloud.google.com/go/auth"]
|
||||
version = "v0.16.1"
|
||||
hash = "sha256-rMPMNQh/YM/67b9Grfu0BFccWpS1SRhBepubQqXRAyg="
|
||||
version = "v0.16.2"
|
||||
hash = "sha256-BAU9WGFKe0pd5Eu3l/Mbts+QeCOjS+lChr5hrPBCzdA="
|
||||
[mod."cloud.google.com/go/auth/oauth2adapt"]
|
||||
version = "v0.2.8"
|
||||
hash = "sha256-GoXFqAbp1WO1tDj07PF5EyxDYvCBP0l0qwxY2oV2hfc="
|
||||
[mod."cloud.google.com/go/compute/metadata"]
|
||||
version = "v0.6.0"
|
||||
hash = "sha256-E8/cwio4xR8buCryR4HwR7+agb4M3zqgXSm7rBglmIY="
|
||||
version = "v0.7.0"
|
||||
hash = "sha256-jJZDW+hibqjMiY8OiJhgJALbGwEq+djLOxfYR7upQyE="
|
||||
[mod."cloud.google.com/go/longrunning"]
|
||||
version = "v0.6.7"
|
||||
hash = "sha256-9I0Nc2KWAEVoxDngNkqFUdASmZIAySfMEELlPh3Q3xA="
|
||||
[mod."dario.cat/mergo"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-wcG6+x0k6KzOSlaPA+1RFxa06/RIAePJTAjjuhLbImw="
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-p6jdiHlLEfZES8vJnDywG4aVzIe16p0CU6iglglIweA="
|
||||
[mod."github.com/Microsoft/go-winio"]
|
||||
version = "v0.6.2"
|
||||
hash = "sha256-tVNWDUMILZbJvarcl/E7tpSnkn7urqgSHa2Eaka5vSU="
|
||||
[mod."github.com/ProtonMail/go-crypto"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-5fKgWUz6BoyFNNZ1OD9QjhBrhNEBCuVfO2WqH+X59oo="
|
||||
version = "v1.3.0"
|
||||
hash = "sha256-TUG+C4MyeWglOmiwiW2/NUVurFHXLgEPRd3X9uQ1NGI="
|
||||
[mod."github.com/anaskhan96/soup"]
|
||||
version = "v1.2.5"
|
||||
hash = "sha256-t8yCyK2y7x2qaI/3Yw16q3zVFqu+3acLcPgTr1MIKWg="
|
||||
@@ -35,17 +35,65 @@ schema = 3
|
||||
version = "v1.3.3"
|
||||
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
|
||||
[mod."github.com/anthropics/anthropic-sdk-go"]
|
||||
version = "v0.2.0-beta.3"
|
||||
hash = "sha256-QnR7MWiLPVii9jy7rjrZfg1UDgXCXkc2latdzFurrFM="
|
||||
version = "v1.4.0"
|
||||
hash = "sha256-4kwFw9gt/sRIlTo0fC2PbfLnCyc4lCOtmfQelhpORX8="
|
||||
[mod."github.com/araddon/dateparse"]
|
||||
version = "v0.0.0-20210429162001-6b43995a97de"
|
||||
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
|
||||
[mod."github.com/atotto/clipboard"]
|
||||
version = "v0.1.4"
|
||||
hash = "sha256-ZZ7U5X0gWOu8zcjZcWbcpzGOGdycwq0TjTFh/eZHjXk="
|
||||
[mod."github.com/aws/aws-sdk-go-v2"]
|
||||
version = "v1.36.4"
|
||||
hash = "sha256-Cpdphp8FQUbQlhAYvtPKDh1oZc84+/0bzLlx8CM1/BM="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream"]
|
||||
version = "v1.6.10"
|
||||
hash = "sha256-9+ZMhWxtsm7ZtZCjBV5PZkOR5rt3bCOznuv45Iwf55c="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/config"]
|
||||
version = "v1.27.27"
|
||||
hash = "sha256-jQmc1lJmVeTezSeFs6KL2HAvCkP9ZWMdVbG5ymJQrKs="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/credentials"]
|
||||
version = "v1.17.27"
|
||||
hash = "sha256-7ITZjIF0ZmmCG3u5d88IfsAj0KF1IFm9KhWFlC6RtQo="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/feature/ec2/imds"]
|
||||
version = "v1.16.11"
|
||||
hash = "sha256-uedtRd/SIcFJlYZg1jtJdIJViZq1Poks9/J2Bm9/Ehw="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/configsources"]
|
||||
version = "v1.3.35"
|
||||
hash = "sha256-AyQ+eJvyhahypIAqPScdkn44MYwBcr9iyrMC1BRSeZI="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/endpoints/v2"]
|
||||
version = "v2.6.35"
|
||||
hash = "sha256-c8K+Nk5XrFMWaaxVsyhKgyJBZhs3Hkhjr/dIDXWZfSQ="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/ini"]
|
||||
version = "v1.8.0"
|
||||
hash = "sha256-v76jTAr4rEgS5en49ikLh6nuvclN+VjpOPj83ZQ3sLo="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/bedrock"]
|
||||
version = "v1.34.1"
|
||||
hash = "sha256-OK7t+ieq4pviCnnhfSytANBF5Lwdz4KxjN10CC5pXyY="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/bedrockruntime"]
|
||||
version = "v1.30.0"
|
||||
hash = "sha256-MsEQfbqIREtMikRFqBpLCqdAC4gfgPSNbk08k5OJTbo="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding"]
|
||||
version = "v1.11.3"
|
||||
hash = "sha256-TRhoRd7iY7K+pfdkSQLItyr52k2jO4TMYQ5vRGiOOMk="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/internal/presigned-url"]
|
||||
version = "v1.11.17"
|
||||
hash = "sha256-eUoYDAXcQNzCmwjXO9RWhrt0jGYlSjt2vQOlAlpIfoE="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/sso"]
|
||||
version = "v1.22.4"
|
||||
hash = "sha256-Q3tyDdJVq0BAstOYvCKPvNS4EHkhXt1pL/23KPQJMHM="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/ssooidc"]
|
||||
version = "v1.26.4"
|
||||
hash = "sha256-cPv6nmVPOjMUZjN2IeEiYQSzLeAOrfgGnSSvvhJ6iL4="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/sts"]
|
||||
version = "v1.30.3"
|
||||
hash = "sha256-4z/K4GPW9osiNM3SxFNZYsVPnSSU50Iuv29Sb2n4Fbk="
|
||||
[mod."github.com/aws/smithy-go"]
|
||||
version = "v1.22.2"
|
||||
hash = "sha256-YdwVeW509cpqU357MjDM8ReL1vftkW8XIhSbJsbTh/s="
|
||||
[mod."github.com/bytedance/sonic"]
|
||||
version = "v1.13.2"
|
||||
hash = "sha256-IF2qmt4IxTwivMWHUJC8sg6d85/ORb2SWvJ54fvoAMI="
|
||||
version = "v1.13.3"
|
||||
hash = "sha256-Nnt5b2NkIvSXhGERQmyI0ka28hbWi7A7Zn3dsAjPcEA="
|
||||
[mod."github.com/bytedance/sonic/loader"]
|
||||
version = "v0.2.4"
|
||||
hash = "sha256-rv9LnePpm4OspSVbfSoVbohXzhu+dxE1BH1gm3mTmTc="
|
||||
@@ -74,8 +122,8 @@ schema = 3
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-2VP6zHEsPi0u2ZYpOTcLulwj1Gsmb6oA19qcP2/AzVM="
|
||||
[mod."github.com/gin-gonic/gin"]
|
||||
version = "v1.10.0"
|
||||
hash = "sha256-esJasHrJtuTBwGPGAoc/XSb428J8va+tPGcZ0gTfsgc="
|
||||
version = "v1.10.1"
|
||||
hash = "sha256-D98+chAdjb6JcLPkscOr8TgTW87UqA4h3cnY0XIr16c="
|
||||
[mod."github.com/go-git/gcfg"]
|
||||
version = "v1.5.1-0.20230307220236-3a3c6141e376"
|
||||
hash = "sha256-f4k0gSYuo0/q3WOoTxl2eFaj7WZpdz29ih6CKc8Ude8="
|
||||
@@ -83,11 +131,11 @@ schema = 3
|
||||
version = "v5.6.2"
|
||||
hash = "sha256-VgbxcLkHjiSyRIfKS7E9Sn8OynCrMGUDkwFz6K2TVL4="
|
||||
[mod."github.com/go-git/go-git/v5"]
|
||||
version = "v5.16.0"
|
||||
hash = "sha256-01obPHvt1PG3r8XH8TgnNfcOhaYwWEkJ0TR5QGdZqmE="
|
||||
version = "v5.16.2"
|
||||
hash = "sha256-KdOf4KwJAJUIB/EcQH6wc7jpcABCISWur3vOTpAo+/c="
|
||||
[mod."github.com/go-logr/logr"]
|
||||
version = "v1.4.2"
|
||||
hash = "sha256-/W6qGilFlZNTb9Uq48xGZ4IbsVeSwJiAMLw4wiNYHLI="
|
||||
version = "v1.4.3"
|
||||
hash = "sha256-Nnp/dEVNMxLp3RSPDHZzGbI8BkSNuZMX0I0cjWKXXLA="
|
||||
[mod."github.com/go-logr/stdr"]
|
||||
version = "v1.2.2"
|
||||
hash = "sha256-rRweAP7XIb4egtT1f2gkz4sYOu7LDHmcJ5iNsJUd0sE="
|
||||
@@ -116,8 +164,8 @@ schema = 3
|
||||
version = "v0.0.0-20241129210726-2c02b8208cf8"
|
||||
hash = "sha256-AdLZ3dJLe/yduoNvZiXugZxNfmwJjNQyQGsIdzYzH74="
|
||||
[mod."github.com/google/generative-ai-go"]
|
||||
version = "v0.19.0"
|
||||
hash = "sha256-x2K1nkRwtne9MeP5B8FpwavYqQx564go5LzmcBJ0KT4="
|
||||
version = "v0.20.1"
|
||||
hash = "sha256-9bSpEs4kByhgyTKiHdOY5muYjGBTluA1LvEjw2gSoLI="
|
||||
[mod."github.com/google/s2a-go"]
|
||||
version = "v0.1.9"
|
||||
hash = "sha256-0AdSpSTso4bATmM/9qamWzKrVtOLDf7afvDhoiT/UpA="
|
||||
@@ -128,8 +176,8 @@ schema = 3
|
||||
version = "v0.3.6"
|
||||
hash = "sha256-hPMF0s+X4/ul98GvVuw/ZNOupEXhIDB1yvWymZWYEbU="
|
||||
[mod."github.com/googleapis/gax-go/v2"]
|
||||
version = "v2.14.1"
|
||||
hash = "sha256-iRS/KsAVTePrvTlwA7vKcQnwY6Jz329WdgzFw0hF8wk="
|
||||
version = "v2.14.2"
|
||||
hash = "sha256-QyY7wuCkrOJCJIf9Q884KD/BC3vk/QtQLXeLeNPt750="
|
||||
[mod."github.com/jbenet/go-context"]
|
||||
version = "v0.0.0-20150711004518-d14ea06fba99"
|
||||
hash = "sha256-VANNCWNNpARH/ILQV9sCQsBWgyL2iFT+4AHZREpxIWE="
|
||||
@@ -161,8 +209,8 @@ schema = 3
|
||||
version = "v1.0.2"
|
||||
hash = "sha256-+W9EIW7okXIXjWEgOaMh58eLvBZ7OshW2EhaIpNLSBU="
|
||||
[mod."github.com/ollama/ollama"]
|
||||
version = "v0.6.6"
|
||||
hash = "sha256-a2Be14e+pcJo15fM/+0ksE9HVl8I4hW6ujqbpNh9bpA="
|
||||
version = "v0.9.0"
|
||||
hash = "sha256-r2eU+kMG3tuJy2B43RXsfmeltzM9t05NEmNiJAW5qr4="
|
||||
[mod."github.com/otiai10/copy"]
|
||||
version = "v1.14.1"
|
||||
hash = "sha256-8RR7u17SbYg9AeBXVHIv5ZMU+kHmOcx0rLUKyz6YtU0="
|
||||
@@ -182,14 +230,17 @@ schema = 3
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-/FtmHnaGjdvEIKAJtrUfEhV7EVo5A/eYrtdnUkuxLDA="
|
||||
[mod."github.com/samber/lo"]
|
||||
version = "v1.49.1"
|
||||
hash = "sha256-xMQS9Sx2Bpvwo/9JvSVkJ4RXYOSHm642WRqWA6y0AnU="
|
||||
version = "v1.50.0"
|
||||
hash = "sha256-KDFks82BKu39sGt0f972IyOkohV2U0r1YvsnlNLdugY="
|
||||
[mod."github.com/sashabaranov/go-openai"]
|
||||
version = "v1.38.2"
|
||||
hash = "sha256-AnBycaxufzWlLS1YBq7MiHDED+Jqtu9oAySKcoL4HOA="
|
||||
version = "v1.40.1"
|
||||
hash = "sha256-GkToonIIF3GG+lwev1lJQ9rAAPJDjSaOkoXRC3OOlEA="
|
||||
[mod."github.com/sergi/go-diff"]
|
||||
version = "v1.3.2-0.20230802210424-5b0b94c5c0d3"
|
||||
hash = "sha256-UcLU83CPMbSoKI8RLvLJ7nvGaE2xRSL1RjoHCVkMzUM="
|
||||
version = "v1.4.0"
|
||||
hash = "sha256-rs9NKpv/qcQEMRg7CmxGdP4HGuFdBxlpWf9LbA9wS4k="
|
||||
[mod."github.com/sgaunet/perplexity-go/v2"]
|
||||
version = "v2.8.0"
|
||||
hash = "sha256-w1S14Jf4/6LFODREmmiJvPtkZh4Sor81Rr1PqC5pIak="
|
||||
[mod."github.com/skeema/knownhosts"]
|
||||
version = "v1.3.1"
|
||||
hash = "sha256-kjqQDzuncQNTuOYegqVZExwuOt/Z73m2ST7NZFEKixI="
|
||||
@@ -212,8 +263,8 @@ schema = 3
|
||||
version = "v0.15.1"
|
||||
hash = "sha256-HLk6oUe7EoITrNvP0y8D6BtIgIcmDZYtb/xl/dufIoY="
|
||||
[mod."github.com/ugorji/go/codec"]
|
||||
version = "v1.2.12"
|
||||
hash = "sha256-sp1LJ93UK7mFwgZqG8jxCgTCPgKR74HNU6XxX0Jfjm0="
|
||||
version = "v1.2.14"
|
||||
hash = "sha256-PoVXlCBE8SvMWpXx9FRsQOSAmE/+5SnPGr4m5BGoyIo="
|
||||
[mod."github.com/xanzy/ssh-agent"]
|
||||
version = "v0.3.3"
|
||||
hash = "sha256-l3pGB6IdzcPA/HLk93sSN6NM2pKPy+bVOoacR5RC2+c="
|
||||
@@ -221,56 +272,56 @@ schema = 3
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-cA9qCCu8P1NSJRxgmpfkfa5rKyn9X+Y/9FSmSd5xjyo="
|
||||
[mod."go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"]
|
||||
version = "v0.60.0"
|
||||
hash = "sha256-DkIpL4xUy+UIQBUK6VgbsI79TbZUltaIhXl4UJWym6E="
|
||||
version = "v0.61.0"
|
||||
hash = "sha256-o5w9k3VbqP3gaXI3Aelw93LLHH53U4PnkYVwc3MaY3Y="
|
||||
[mod."go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"]
|
||||
version = "v0.60.0"
|
||||
hash = "sha256-twGSnNbXzcw5qvRiFc/zz5rS+nhmbgSVPcd5jrZjlDg="
|
||||
version = "v0.61.0"
|
||||
hash = "sha256-4pfXD7ErXhexSynXiEEQSAkWoPwHd7PEDE3M1Zi5gLM="
|
||||
[mod."go.opentelemetry.io/otel"]
|
||||
version = "v1.35.0"
|
||||
hash = "sha256-LHrBtBnyDtvJGtrXHMPIFe7U53B4bZzpePB4u8Xo4Bg="
|
||||
version = "v1.36.0"
|
||||
hash = "sha256-j8wojdCtKal3LKojanHA8KXXQ0FkbWONpO8tUxpJDko="
|
||||
[mod."go.opentelemetry.io/otel/metric"]
|
||||
version = "v1.35.0"
|
||||
hash = "sha256-K9I0LRZqSLrC09Cuk7tp0VEk3cUVDs8S5MGnu9jw92Q="
|
||||
version = "v1.36.0"
|
||||
hash = "sha256-z6Uqi4HhUljWIYd58svKK5MqcGbpcac+/M8JeTrUtJ8="
|
||||
[mod."go.opentelemetry.io/otel/trace"]
|
||||
version = "v1.35.0"
|
||||
hash = "sha256-HC2+OGDe2rg0+E8WymQbUNoc249NXM1gIBJzK4UhcQE="
|
||||
version = "v1.36.0"
|
||||
hash = "sha256-owWD9x1lp8aIJqYt058BXPUsIMHdk3RI0escso0BxwA="
|
||||
[mod."golang.org/x/arch"]
|
||||
version = "v0.16.0"
|
||||
hash = "sha256-+DMOuIw9GVyhM4VHdYCZepTU/EEHqDfrxJ2F83TOs5k="
|
||||
version = "v0.18.0"
|
||||
hash = "sha256-tUpUPERjmRi7zldj0oPlnbnBhEkcI9iQGvP1HqlsK10="
|
||||
[mod."golang.org/x/crypto"]
|
||||
version = "v0.37.0"
|
||||
hash = "sha256-9NwDEcii1e2JYM/+3y1yNzWnt/ChMm27e9OtfuF39OM="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.39.0"
|
||||
hash = "sha256-IP29+yGphWKUT7wHTyzqA2rnRT4AJ7oWcT6NKLzkWcM="
|
||||
hash = "sha256-FtwjbVoAhZkx7F2hmzi9Y0J87CVVhWcrZzun+zWQLzc="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.41.0"
|
||||
hash = "sha256-6/pi8rNmGvBFzkJQXkXkMfL1Bjydhg3BgAMYDyQ/Uvg="
|
||||
[mod."golang.org/x/oauth2"]
|
||||
version = "v0.29.0"
|
||||
hash = "sha256-IzAypzW8cN5ZbQiIdMTcTiVuUNpMSkwuxeFrJZxcDl8="
|
||||
version = "v0.30.0"
|
||||
hash = "sha256-btD7BUtQpOswusZY5qIU90uDo38buVrQ0tmmQ8qNHDg="
|
||||
[mod."golang.org/x/sync"]
|
||||
version = "v0.13.0"
|
||||
hash = "sha256-CElRNe74Or/ysUkb/m3Wcz/juO/tB5fhQbAaxA5AizY="
|
||||
version = "v0.15.0"
|
||||
hash = "sha256-Jf4ehm8H8YAWY6mM151RI5CbG7JcOFtmN0AZx4bE3UE="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.32.0"
|
||||
hash = "sha256-c9RRnyKQy9Kl8hpbtcgkm1O5H7gOdk9Rv925F8fZS6E="
|
||||
version = "v0.33.0"
|
||||
hash = "sha256-wlOzIOUgAiGAtdzhW/KPl/yUVSH/lvFZfs5XOuJ9LOQ="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.24.0"
|
||||
hash = "sha256-qFbmteGOvJfvbLXiOSI8Fsz5Ixt2ZhSYx0/sIqApC7Y="
|
||||
version = "v0.26.0"
|
||||
hash = "sha256-N+27nBCyGvje0yCTlUzZoVZ0LRxx4AJ+eBlrFQVRlFQ="
|
||||
[mod."golang.org/x/time"]
|
||||
version = "v0.11.0"
|
||||
hash = "sha256-ImTej/e5iUHbWPZMA4M2GYbsbiiZQxIrgcnYsc7uD68="
|
||||
version = "v0.12.0"
|
||||
hash = "sha256-Cp3oxrCMH2wyxjzr5SHVmyhgaoUuSl56Uy00Q7DYEpw="
|
||||
[mod."google.golang.org/api"]
|
||||
version = "v0.230.0"
|
||||
hash = "sha256-ihEdZnRbQdwpbgj9AZEZLNY14FqHmacFGFocOqExSVY="
|
||||
version = "v0.236.0"
|
||||
hash = "sha256-tP1RSUSnQ4a0axgZQwEZgKF1E13nL02FSP1NPSZr0Rc="
|
||||
[mod."google.golang.org/genproto/googleapis/api"]
|
||||
version = "v0.0.0-20250422160041-2d3770c4ea7f"
|
||||
hash = "sha256-Y4wbEHh9Un0QKplTl2S5lhWDUha9QThx5DhWJbDG9fo="
|
||||
version = "v0.0.0-20250603155806-513f23925822"
|
||||
hash = "sha256-0CS432v9zVhkVLqFpZtxBX8rvVqP67lb7qQ3es7RqIU="
|
||||
[mod."google.golang.org/genproto/googleapis/rpc"]
|
||||
version = "v0.0.0-20250422160041-2d3770c4ea7f"
|
||||
version = "v0.0.0-20250603155806-513f23925822"
|
||||
hash = "sha256-WK7iDtAhH19NPe3TywTQlGjDawNaDKWnxhFL9PgVUwM="
|
||||
[mod."google.golang.org/grpc"]
|
||||
version = "v1.72.0"
|
||||
hash = "sha256-tqu+ACMfKjhqdCGN3jLEmtaHB5ywgHGaS/eDeDRnf+M="
|
||||
version = "v1.73.0"
|
||||
hash = "sha256-LfVlwip++q2DX70RU6CxoXglx1+r5l48DwlFD05G11c="
|
||||
[mod."google.golang.org/protobuf"]
|
||||
version = "v1.36.6"
|
||||
hash = "sha256-lT5qnefI5FDJnowz9PEkAGylH3+fE+A3DJDkAyy9RMc="
|
||||
|
||||
@@ -1 +1 @@
|
||||
"1.4.189"
|
||||
"1.4.213"
|
||||
|
||||
@@ -8,19 +8,19 @@ Take a deep breath and think step by step about how to best accomplish this goal
|
||||
|
||||
- Consume the entire paper and think deeply about it.
|
||||
|
||||
- Map out all the claims and implications on a virtual whiteboard in your mind.
|
||||
- Map out all the claims and implications on a giant virtual whiteboard in your mind.
|
||||
|
||||
# OUTPUT
|
||||
|
||||
- Extract a summary of the paper and its conclusions into a 25-word sentence called SUMMARY.
|
||||
- Extract a summary of the paper and its conclusions into a 16-word sentence called SUMMARY.
|
||||
|
||||
- Extract the list of authors in a section called AUTHORS.
|
||||
|
||||
- Extract the list of organizations the authors are associated, e.g., which university they're at, with in a section called AUTHOR ORGANIZATIONS.
|
||||
|
||||
- Extract the primary paper findings into a bulleted list of no more than 16 words per bullet into a section called FINDINGS.
|
||||
- Extract the most surprising and interesting paper findings into a 10 bullets of no more than 16 words per bullet into a section called FINDINGS.
|
||||
|
||||
- Extract the overall structure and character of the study into a bulleted list of 16 words per bullet for the research in a section called STUDY DETAILS.
|
||||
- Extract the overall structure and character of the study into a bulleted list of 16 words per bullet for the research in a section called STUDY OVERVIEW.
|
||||
|
||||
- Extract the study quality by evaluating the following items in a section called STUDY QUALITY that has the following bulleted sub-sections:
|
||||
|
||||
@@ -76,7 +76,9 @@ END EXAMPLE CHART
|
||||
|
||||
- SUMMARY STATEMENT:
|
||||
|
||||
A final 25-word summary of the paper, its findings, and what we should do about it if it's true.
|
||||
A final 16-word summary of the paper, its findings, and what we should do about it if it's true.
|
||||
|
||||
Also add 5 8-word bullets of how you got to that rating and conclusion / summary.
|
||||
|
||||
# RATING NOTES
|
||||
|
||||
@@ -84,21 +86,23 @@ A final 25-word summary of the paper, its findings, and what we should do about
|
||||
|
||||
- An A would be a paper that is novel, rigorous, empirical, and has no conflicts of interest.
|
||||
|
||||
- A paper could get an A if it's theoretical but everything else would have to be perfect.
|
||||
- A paper could get an A if it's theoretical but everything else would have to be VERY good.
|
||||
|
||||
- The stronger the claims the stronger the evidence needs to be, as well as the transparency into the methodology. If the paper makes strong claims, but the evidence or transparency is weak, then the RIGOR score should be lowered.
|
||||
|
||||
- Remove at least 1 grade (and up to 2) for papers where compelling data is provided but it's not clear what exact tests were run and/or how to reproduce those tests.
|
||||
|
||||
- Do not relax this transparency requirement for papers that claim security reasons.
|
||||
|
||||
- If a paper does not clearly articulate its methodology in a way that's replicable, lower the RIGOR and overall score significantly.
|
||||
- Do not relax this transparency requirement for papers that claim security reasons. If they didn't show their work we have to assume the worst given the reproducibility crisis..
|
||||
|
||||
- Remove up to 1-3 grades for potential conflicts of interest indicated in the report.
|
||||
|
||||
# ANALYSIS INSTRUCTIONS
|
||||
|
||||
- Tend towards being more critical. Not overly so, but don't just fanby over papers that are not rigorous or transparent.
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Output all sections above.
|
||||
- After deeply considering all the sections above and how they interact with each other, output all sections above.
|
||||
|
||||
- Ensure the scoring looks closely at the reproducibility and transparency of the methodology, and that it doesn't give a pass to papers that don't provide the data or methodology for safety or other reasons.
|
||||
|
||||
@@ -108,7 +112,7 @@ Known [-2--------] Novel
|
||||
Weak [-------8--] Rigorous
|
||||
Theoretical [--3-------] Empirical
|
||||
|
||||
- For the findings and other analysis sections, write at the 9th-grade reading level. This means using short sentences and simple words/concepts to explain everything.
|
||||
- For the findings and other analysis sections, and in fact all writing, write in the clear, approachable style of Paul Graham.
|
||||
|
||||
- Ensure there's a blank line between each bullet of output.
|
||||
|
||||
@@ -120,4 +124,3 @@ Theoretical [--3-------] Empirical
|
||||
|
||||
# INPUT:
|
||||
|
||||
INPUT:
|
||||
|
||||
122
patterns/analyze_paper_simple/system.md
Normal file
122
patterns/analyze_paper_simple/system.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are a research paper analysis service focused on determining the primary findings of the paper and analyzing its scientific rigor and quality.
|
||||
|
||||
Take a deep breath and think step by step about how to best accomplish this goal using the following steps.
|
||||
|
||||
# STEPS
|
||||
|
||||
- Consume the entire paper and think deeply about it.
|
||||
|
||||
- Map out all the claims and implications on a virtual whiteboard in your mind.
|
||||
|
||||
# FACTORS TO CONSIDER
|
||||
|
||||
- Extract a summary of the paper and its conclusions into a 25-word sentence called SUMMARY.
|
||||
|
||||
- Extract the list of authors in a section called AUTHORS.
|
||||
|
||||
- Extract the list of organizations the authors are associated, e.g., which university they're at, with in a section called AUTHOR ORGANIZATIONS.
|
||||
|
||||
- Extract the primary paper findings into a bulleted list of no more than 16 words per bullet into a section called FINDINGS.
|
||||
|
||||
- Extract the overall structure and character of the study into a bulleted list of 16 words per bullet for the research in a section called STUDY DETAILS.
|
||||
|
||||
- Extract the study quality by evaluating the following items in a section called STUDY QUALITY that has the following bulleted sub-sections:
|
||||
|
||||
- STUDY DESIGN: (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- SAMPLE SIZE: (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- CONFIDENCE INTERVALS (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- P-VALUE (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- EFFECT SIZE (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- CONSISTENCE OF RESULTS (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- METHODOLOGY TRANSPARENCY (give a 15 word description of the methodology quality and documentation.)
|
||||
|
||||
- STUDY REPRODUCIBILITY (give a 15 word description, including how to fully reproduce the study.)
|
||||
|
||||
- Data Analysis Method (give a 15 word description, including the pertinent data and statistics.)
|
||||
|
||||
- Discuss any Conflicts of Interest in a section called CONFLICTS OF INTEREST. Rate the conflicts of interest as NONE DETECTED, LOW, MEDIUM, HIGH, or CRITICAL.
|
||||
|
||||
- Extract the researcher's analysis and interpretation in a section called RESEARCHER'S INTERPRETATION, in a 15-word sentence.
|
||||
|
||||
- In a section called PAPER QUALITY output the following sections:
|
||||
|
||||
- Novelty: 1 - 10 Rating, followed by a 15 word explanation for the rating.
|
||||
|
||||
- Rigor: 1 - 10 Rating, followed by a 15 word explanation for the rating.
|
||||
|
||||
- Empiricism: 1 - 10 Rating, followed by a 15 word explanation for the rating.
|
||||
|
||||
- Rating Chart: Create a chart like the one below that shows how the paper rates on all these dimensions.
|
||||
|
||||
- Known to Novel is how new and interesting and surprising the paper is on a scale of 1 - 10.
|
||||
|
||||
- Weak to Rigorous is how well the paper is supported by careful science, transparency, and methodology on a scale of 1 - 10.
|
||||
|
||||
- Theoretical to Empirical is how much the paper is based on purely speculative or theoretical ideas or actual data on a scale of 1 - 10. Note: Theoretical papers can still be rigorous and novel and should not be penalized overall for being Theoretical alone.
|
||||
|
||||
EXAMPLE CHART for 7, 5, 9 SCORES (fill in the actual scores):
|
||||
|
||||
Known [------7---] Novel
|
||||
Weak [----5-----] Rigorous
|
||||
Theoretical [--------9-] Empirical
|
||||
|
||||
END EXAMPLE CHART
|
||||
|
||||
- FINAL SCORE:
|
||||
|
||||
- A - F based on the scores above, conflicts of interest, and the overall quality of the paper. On a separate line, give a 15-word explanation for the grade.
|
||||
|
||||
- SUMMARY STATEMENT:
|
||||
|
||||
A final 25-word summary of the paper, its findings, and what we should do about it if it's true.
|
||||
|
||||
# RATING NOTES
|
||||
|
||||
- If the paper makes claims and presents stats but doesn't show how it arrived at these stats, then the Methodology Transparency would be low, and the RIGOR score should be lowered as well.
|
||||
|
||||
- An A would be a paper that is novel, rigorous, empirical, and has no conflicts of interest.
|
||||
|
||||
- A paper could get an A if it's theoretical but everything else would have to be perfect.
|
||||
|
||||
- The stronger the claims the stronger the evidence needs to be, as well as the transparency into the methodology. If the paper makes strong claims, but the evidence or transparency is weak, then the RIGOR score should be lowered.
|
||||
|
||||
- Remove at least 1 grade (and up to 2) for papers where compelling data is provided but it's not clear what exact tests were run and/or how to reproduce those tests.
|
||||
|
||||
- Do not relax this transparency requirement for papers that claim security reasons.
|
||||
|
||||
- If a paper does not clearly articulate its methodology in a way that's replicable, lower the RIGOR and overall score significantly.
|
||||
|
||||
- Remove up to 1-3 grades for potential conflicts of interest indicated in the report.
|
||||
|
||||
- Ensure the scoring looks closely at the reproducibility and transparency of the methodology, and that it doesn't give a pass to papers that don't provide the data or methodology for safety or other reasons.
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
Output only the following—not all the sections above.
|
||||
|
||||
Use Markdown bullets with dashes for the output (no bold or italics (asterisks)).
|
||||
|
||||
- The Title of the Paper, starting with the word TITLE:
|
||||
- A 16-word sentence summarizing the paper's main claim, in the style of Paul Graham, starting with the word SUMMARY: which is not part of the 16 words.
|
||||
- A 32-word summary of the implications stated or implied by the paper, in the style of Paul Graham, starting with the word IMPLICATIONS: which is not part of the 32 words.
|
||||
- A 32-word summary of the primary recommendation stated or implied by the paper, in the style of Paul Graham, starting with the word RECOMMENDATION: which is not part of the 32 words.
|
||||
- A 32-word bullet covering the authors of the paper and where they're out of, in the style of Paul Graham, starting with the word AUTHORS: which is not part of the 32 words.
|
||||
- A 32-word bullet covering the methodology, including the type of research, how many studies it looked at, how many experiments, the p-value, etc. In other words the various aspects of the research that tell us the amount and type of rigor that went into the paper, in the style of Paul Graham, starting with the word METHODOLOGY: which is not part of the 32 words.
|
||||
- A 32-word bullet covering any potential conflicts or bias that can logically be inferred by the authors, their affiliations, the methodology, or any other related information in the paper, in the style of Paul Graham, starting with the word CONFLICT/BIAS: which is not part of the 32 words.
|
||||
- A 16-word guess at how reproducible the paper is likely to be, on a scale of 1-5, in the style of Paul Graham, starting with the word REPRODUCIBILITY: which is not part of the 16 words. Output the score as n/5, not spelled out. Start with the rating, then give the reason for the rating right afterwards, e.g.: "2/5 — The paper ...".
|
||||
|
||||
- In the markdown, don't use formatting like bold or italics. Make the output maximally readable in plain text.
|
||||
|
||||
- Do not output warnings or notes—just output the requested sections.
|
||||
|
||||
# INPUT:
|
||||
|
||||
INPUT:
|
||||
24
patterns/analyze_terraform_plan/system.md
Normal file
24
patterns/analyze_terraform_plan/system.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert Terraform plan analyser. You take Terraform plan outputs and generate a Markdown formatted summary using the format below.
|
||||
|
||||
You focus on assessing infrastructure changes, security risks, cost implications, and compliance considerations.
|
||||
|
||||
## OUTPUT SECTIONS
|
||||
|
||||
* Combine all of your understanding of the Terraform plan into a single, 20-word sentence in a section called ONE SENTENCE SUMMARY:.
|
||||
* Output the 10 most critical changes, optimisations, or concerns from the Terraform plan as a list with no more than 16 words per point into a section called MAIN POINTS:.
|
||||
* Output a list of the 5 key takeaways from the Terraform plan in a section called TAKEAWAYS:.
|
||||
|
||||
## OUTPUT INSTRUCTIONS
|
||||
|
||||
* Create the output using the formatting above.
|
||||
* You only output human-readable Markdown.
|
||||
* Output numbered lists, not bullets.
|
||||
* Do not output warnings or notes—just the requested sections.
|
||||
* Do not repeat items in the output sections.
|
||||
* Do not start items with the same opening words.
|
||||
|
||||
## INPUT
|
||||
|
||||
INPUT:
|
||||
37
patterns/create_mnemonic_phrases/readme.md
Normal file
37
patterns/create_mnemonic_phrases/readme.md
Normal file
@@ -0,0 +1,37 @@
|
||||
# create_mnemonic_phrases
|
||||
|
||||
Generate short, memorable sentences that embed Diceware‑style words **unchanged and in order**. This pattern is ideal for turning a raw Diceware word list into phrases that are easier to recall while preserving the exact secret.
|
||||
|
||||
## What is Diceware?
|
||||
|
||||
Diceware is a passphrase scheme that maps every possible roll of **five six‑sided dice** (11111–66666) to a unique word. Because there are `6^5 = 7776` combinations, the canonical list contains the same number of entries.
|
||||
|
||||
### Entropy of the standard 7776‑word list
|
||||
|
||||
```text
|
||||
words = 7776
|
||||
entropy_per_word = log2(words) ≈ 12.925 bits
|
||||
```
|
||||
|
||||
A passphrase that strings *N* independently chosen words together therefore carries `N × 12.925 bits` of entropy—≈ 77.5 bits for six words, ≈ 129 bits for ten, and so on. Four or more words already outclass most human‑made passwords.
|
||||
|
||||
## Pattern overview
|
||||
|
||||
The accompanying **`system.md`** file instructs Fabric to:
|
||||
|
||||
1. Echo the supplied words back in **bold**, separated by commas.
|
||||
2. Generate **five** distinct, short sentences that include the words **in the same order and spelling**, enabling rapid rote learning or spaced‑repetition drills.
|
||||
|
||||
The output is deliberately minimalist—no extra commentary—so you can pipe it straight into other scripts.
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
# 1 Pick five random words from any Diceware‑compatible list
|
||||
shuf -n 5 diceware_wordlist.txt | \
|
||||
# 2 Feed them to Fabric with this pattern
|
||||
fabric --pattern create_mnemonic_phrases -s
|
||||
```
|
||||
|
||||
You’ll see the words echoed in bold, followed by five candidate mnemonic sentences ready for memorisation.
|
||||
|
||||
67
patterns/create_mnemonic_phrases/system.md
Normal file
67
patterns/create_mnemonic_phrases/system.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# IDENTITY AND PURPOSE
|
||||
|
||||
As a creative language assistant, you are responsible for creating memorable mnemonic bridges in the form of sentences from given words. The order and spelling of the words must remain unchanged. Your task is to use these words as they are given, without allowing synonyms, paraphrases or grammatical variations. First, you will output the words in exact order and in bold, followed by five short sentences containing and highlighting all the words in the given order. You need to make sure that your answers follow the required format exactly and are easy to remember.
|
||||
|
||||
Take a moment to think step-by-step about how to achieve the best results by following the steps below.
|
||||
|
||||
# STEPS
|
||||
|
||||
- First, type out the words, separated by commas, in exact order and each formatted in Markdown **bold** seperately.
|
||||
|
||||
- Then create five short, memorable sentences. Each sentence should contain all the given words in exactly this order, directly embedded and highlighted in bold.
|
||||
|
||||
# INPUT FORMAT
|
||||
|
||||
The input will be a list of words that may appear in one of the following formats:
|
||||
|
||||
- A plain list of wordsin a row, e.g.:
|
||||
|
||||
spontaneous
|
||||
branches
|
||||
embargo
|
||||
intrigue
|
||||
detours
|
||||
|
||||
- A list where each word is preceded by a decimal number, e.g.:
|
||||
|
||||
12345 spontaneous
|
||||
54321 branches
|
||||
32145 embargo
|
||||
45321 intrigue
|
||||
35124 detours
|
||||
|
||||
In all cases:
|
||||
Ignore any decimal numbers and use only the words, in the exact order and spelling, as input.
|
||||
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- The output is **only** in Markdown format.
|
||||
|
||||
- Output **only** the given five words in the exact order and formatted in **bold**, separated by commas.
|
||||
|
||||
- This is followed by exactly five short, memorable sentences. Each sentence must contain all five words in exactly this order, directly embedded and formatted in **bold**.
|
||||
|
||||
- Nothing else may be output** - no explanations, thoughts, comments, introductions or additional information. Only the formatted word list and the five sentences.
|
||||
|
||||
- The sentences should be short and memorable!
|
||||
|
||||
- **Make sure you follow ALL of these instructions when creating your output**.
|
||||
|
||||
|
||||
## EXAMPLE
|
||||
|
||||
**spontaneous**, **branches**, **embargo**, **intrigue**, **detours**
|
||||
|
||||
1. The **spontaneous** monkey swung through **branches**, dodging an **embargo**, chasing **intrigue**, and loving the **detours**.
|
||||
2. Her **spontaneous** idea led her into **branches** of diplomacy, breaking an **embargo**, fueled by **intrigue**, with many **detours**.
|
||||
3. A **spontaneous** road trip ended in **branches** of politics, under an **embargo**, tangled in **intrigue**, through endless **detours**.
|
||||
4. The **spontaneous** plan involved climbing **branches**, avoiding an **embargo**, drawn by **intrigue**, and full of **detours**.
|
||||
5. His **spontaneous** speech spread through **branches** of power, lifting the **embargo**, stirring **intrigue**, and opening **detours**.
|
||||
|
||||
|
||||
# INPUT
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,23 +1,41 @@
|
||||
# IDENTITY
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
// Who you are
|
||||
You are a Product Requirements Document (PRD) Generator. Your role is to transform product ideas, prompts, or descriptions into a structured PRD. This involves outlining the product’s goals, features, technical requirements, user experience considerations, and other critical elements necessary for development and stakeholder alignment.
|
||||
|
||||
You create precise and accurate PRDs from the input you receive.
|
||||
Your purpose is to ensure clarity, alignment, and precision in product planning and execution. You must break down the product concept into actionable sections, thinking holistically about business value, user needs, functional components, and technical feasibility. Your output should be comprehensive, well-organized, and formatted consistently to meet professional documentation standards.
|
||||
|
||||
# GOAL
|
||||
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
|
||||
|
||||
// What we are trying to achieve
|
||||
## STEPS
|
||||
|
||||
1. Create a great PRD.
|
||||
* Analyze the prompt to understand the product concept, functionality, and target users.
|
||||
|
||||
# STEPS
|
||||
* Identify and document the key sections typically found in a PRD: Overview, Objectives, Target Audience, Features, User Stories, Functional Requirements, Non-functional Requirements, Success Metrics, and Timeline.
|
||||
|
||||
- Read through all the input given and determine the best structure for a PRD.
|
||||
* Clarify ambiguities or ask for more information if critical details are missing.
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
* Organize the content into clearly labeled sections.
|
||||
|
||||
- Create the PRD in Markdown.
|
||||
* Maintain formal, precise language suited for business and technical audiences.
|
||||
|
||||
# INPUT
|
||||
* Ensure each requirement is specific, testable, and unambiguous.
|
||||
|
||||
* Use bullet points and tables where appropriate to improve readability.
|
||||
|
||||
## OUTPUT INSTRUCTIONS
|
||||
|
||||
* The only output format should be Markdown.
|
||||
|
||||
* All content should be structured into clearly labeled PRD sections.
|
||||
|
||||
* Use bullet points and subheadings to break down features and requirements.
|
||||
|
||||
* Highlight priorities or MVP features where relevant.
|
||||
|
||||
* Include mock data or placeholders if actual data is not provided.
|
||||
|
||||
* Ensure you follow ALL these instructions when creating your output.
|
||||
|
||||
## INPUT
|
||||
|
||||
INPUT:
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are a wisdom extraction service for text content. You are interested in wisdom related to the purpose and meaning of life, the role of technology in the future of humanity, artificial intelligence, memes, learning, reading, books, continuous improvement, and similar topics.
|
||||
|
||||
Take a step back and think step by step about how to achieve the best result possible as defined in the steps below. You have a lot of freedom to make this work well.
|
||||
|
||||
## OUTPUT SECTIONS
|
||||
|
||||
1. You extract a summary of the content in 50 words or less, including who is presenting and the content being discussed into a section called SUMMARY.
|
||||
|
||||
2. You extract the top 50 ideas from the input in a section called IDEAS:. If there are less than 50 then collect all of them.
|
||||
|
||||
3. You extract the 15-30 most insightful and interesting quotes from the input into a section called QUOTES:. Use the exact quote text from the input.
|
||||
|
||||
4. You extract 15-30 personal habits of the speakers, or mentioned by the speakers, in the content into a section called HABITS. Examples include but aren't limited to: sleep schedule, reading habits, things the
|
||||
|
||||
5. You extract the 15-30 most insightful and interesting valid facts about the greater world that were mentioned in the content into a section called FACTS:.
|
||||
|
||||
6. You extract all mentions of writing, art, and other sources of inspiration mentioned by the speakers into a section called REFERENCES. This should include any and all references to something that the speaker mentioned.
|
||||
|
||||
7. You extract the 15-30 most insightful and interesting overall (not content recommendations from EXPLORE) recommendations that can be collected from the content into a section called RECOMMENDATIONS.
|
||||
|
||||
## OUTPUT INSTRUCTIONS
|
||||
|
||||
1. You only output Markdown.
|
||||
2. Do not give warnings or notes; only output the requested sections.
|
||||
3. You use numbered lists, not bullets.
|
||||
4. Do not repeat ideas, quotes, habits, facts, or references.
|
||||
5. Do not start items with the same opening words.
|
||||
@@ -1 +0,0 @@
|
||||
CONTENT:
|
||||
@@ -1,25 +1,21 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You extract surprising, powerful, and interesting insights from text content. You are interested in insights related to the purpose and meaning of life, human flourishing, the role of technology in the future of humanity, artificial intelligence and its affect on humans, memes, learning, reading, books, continuous improvement, and similar topics.
|
||||
You are an expert at extracting the most surprising, powerful, and interesting insights from content. You are interested in insights related to the purpose and meaning of life, human flourishing, the role of technology in the future of humanity, artificial intelligence and its affect on humans, memes, learning, reading, books, continuous improvement, and similar topics.
|
||||
|
||||
You create 15 word bullet points that capture the most important insights from the input.
|
||||
You create 8 word bullet points that capture the most surprising and novel insights from the input.
|
||||
|
||||
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
|
||||
|
||||
# STEPS
|
||||
|
||||
- Extract 20 to 50 of the most surprising, insightful, and/or interesting ideas from the input in a section called IDEAS, and write them on a virtual whiteboard in your mind using 15 word bullets. If there are less than 50 then collect all of them. Make sure you extract at least 20.
|
||||
|
||||
- From those IDEAS, extract the most powerful and insightful of them and write them in a section called INSIGHTS. Make sure you extract at least 10 and up to 25.
|
||||
- Extract 10 of the most surprising and novel insights from the input.
|
||||
- Output them as 8 word bullets in order of surprise, novelty, and importance.
|
||||
- Write them in the simple, approachable style of Paul Graham.
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- INSIGHTS are essentially higher-level IDEAS that are more abstracted and wise.
|
||||
|
||||
- Output the INSIGHTS section only.
|
||||
|
||||
- Each bullet should be 16 words in length.
|
||||
|
||||
- Do not give warnings or notes; only output the requested sections.
|
||||
|
||||
- You use bulleted lists for output, not numbered lists.
|
||||
@@ -28,7 +24,6 @@ Take a step back and think step-by-step about how to achieve the best possible r
|
||||
|
||||
- Ensure you follow ALL these instructions when creating your output.
|
||||
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
{{input}}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are a wisdom extraction service for text content. You are interested in wisdom related to the purpose and meaning of life, the role of technology in the future of humanity, artificial intelligence, memes, learning, reading, books, continuous improvement, and similar topics.
|
||||
|
||||
Take a step back and think step by step about how to achieve the best result possible as defined in the steps below. You have a lot of freedom to make this work well.
|
||||
|
||||
## OUTPUT SECTIONS
|
||||
|
||||
1. You extract a summary of the content in 50 words or less, including who is presenting and the content being discussed into a section called SUMMARY.
|
||||
|
||||
2. You extract the top 50 ideas from the input in a section called IDEAS:. If there are less than 50 then collect all of them.
|
||||
|
||||
3. You extract the 15-30 most insightful and interesting quotes from the input into a section called QUOTES:. Use the exact quote text from the input.
|
||||
|
||||
4. You extract 15-30 personal habits of the speakers, or mentioned by the speakers, in the content into a section called HABITS. Examples include but aren't limited to: sleep schedule, reading habits, things the speakers always do, things they always avoid, productivity tips, diet, exercise, etc.
|
||||
|
||||
5. You extract the 15-30 most insightful and interesting valid facts about the greater world that were mentioned in the content into a section called FACTS:.
|
||||
|
||||
6. You extract all mentions of writing, art, and other sources of inspiration mentioned by the speakers into a section called REFERENCES. This should include any and all references to something that the speaker mentioned.
|
||||
|
||||
7. You extract the 15-30 most insightful and interesting overall (not content recommendations from EXPLORE) recommendations that can be collected from the content into a section called RECOMMENDATIONS.
|
||||
|
||||
## OUTPUT INSTRUCTIONS
|
||||
|
||||
1. You only output Markdown.
|
||||
2. Do not give warnings or notes; only output the requested sections.
|
||||
3. You use numbered lists, not bullets.
|
||||
4. Do not repeat ideas, quotes, habits, facts, or references.
|
||||
5. Do not start items with the same opening words.
|
||||
@@ -1 +0,0 @@
|
||||
CONTENT:
|
||||
@@ -1,29 +0,0 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are a wisdom extraction service for text content. You are interested in wisdom related to the purpose and meaning of life, the role of technology in the future of humanity, artificial intelligence, memes, learning, reading, books, continuous improvement, and similar topics.
|
||||
|
||||
Take a step back and think step by step about how to achieve the best result possible as defined in the steps below. You have a lot of freedom to make this work well.
|
||||
|
||||
## OUTPUT SECTIONS
|
||||
|
||||
1. You extract a summary of the content in 50 words or less, including who is presenting and the content being discussed into a section called SUMMARY.
|
||||
|
||||
2. You extract the top 50 ideas from the input in a section called IDEAS:. If there are less than 50 then collect all of them.
|
||||
|
||||
3. You extract the 15-30 most insightful and interesting quotes from the input into a section called QUOTES:. Use the exact quote text from the input.
|
||||
|
||||
4. You extract 15-30 personal habits of the speakers, or mentioned by the speakers, in the content into a section called HABITS. Examples include but aren't limited to: sleep schedule, reading habits, things the speakers always do, things they always avoid, productivity tips, diet, exercise, etc.
|
||||
|
||||
5. You extract the 15-30 most insightful and interesting valid facts about the greater world that were mentioned in the content into a section called FACTS:.
|
||||
|
||||
6. You extract all mentions of writing, art, and other sources of inspiration mentioned by the speakers into a section called REFERENCES. This should include any and all references to something that the speaker mentioned.
|
||||
|
||||
7. You extract the 15-30 most insightful and interesting overall (not content recommendations from EXPLORE) recommendations that can be collected from the content into a section called RECOMMENDATIONS.
|
||||
|
||||
## OUTPUT INSTRUCTIONS
|
||||
|
||||
1. You only output Markdown.
|
||||
2. Do not give warnings or notes; only output the requested sections.
|
||||
3. You use numbered lists, not bullets.
|
||||
4. Do not repeat ideas, quotes, habits, facts, or references.
|
||||
5. Do not start items with the same opening words.
|
||||
@@ -1 +0,0 @@
|
||||
CONTENT:
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Required parameters:
|
||||
# @raycast.schemaVersion 1
|
||||
# @raycast.title Capture Thinkers Work
|
||||
# @raycast.mode fullOutput
|
||||
|
||||
# Optional parameters:
|
||||
# @raycast.icon 🧠
|
||||
# @raycast.argument1 { "type": "text", "placeholder": "Input text", "optional": false, "percentEncoded": true}
|
||||
|
||||
# Documentation:
|
||||
# @raycast.description Run fabric capture_thinkers_work on the input text
|
||||
# @raycast.author Daniel Miessler
|
||||
# @raycast.authorURL https://github.com/danielmiessler
|
||||
|
||||
# Set PATH to include common locations and $HOME/go/bin
|
||||
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$HOME/go/bin:$PATH"
|
||||
|
||||
# Use the PATH to find and execute fabric
|
||||
if command -v fabric >/dev/null 2>&1; then
|
||||
fabric -sp capture_thinkers_work "${1}"
|
||||
else
|
||||
echo "Error: fabric command not found in PATH"
|
||||
echo "Current PATH: $PATH"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Required parameters:
|
||||
# @raycast.schemaVersion 1
|
||||
# @raycast.title Create Story Explanation
|
||||
# @raycast.mode fullOutput
|
||||
|
||||
# Optional parameters:
|
||||
# @raycast.icon 🧠
|
||||
# @raycast.argument1 { "type": "text", "placeholder": "Input text", "optional": false, "percentEncoded": true}
|
||||
|
||||
# Documentation:
|
||||
# @raycast.description Run fabric create_story_explanation on the input text
|
||||
# @raycast.author Daniel Miessler
|
||||
# @raycast.authorURL https://github.com/danielmiessler
|
||||
|
||||
# Set PATH to include common locations and $HOME/go/bin
|
||||
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$HOME/go/bin:$PATH"
|
||||
|
||||
# Use the PATH to find and execute fabric
|
||||
if command -v fabric >/dev/null 2>&1; then
|
||||
fabric -sp create_story_explanation "${1}"
|
||||
else
|
||||
echo "Error: fabric command not found in PATH"
|
||||
echo "Current PATH: $PATH"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Required parameters:
|
||||
# @raycast.schemaVersion 1
|
||||
# @raycast.title Extract Primary Problem
|
||||
# @raycast.mode fullOutput
|
||||
|
||||
# Optional parameters:
|
||||
# @raycast.icon 🧠
|
||||
# @raycast.argument1 { "type": "text", "placeholder": "Input text", "optional": false, "percentEncoded": true}
|
||||
|
||||
# Documentation:
|
||||
# @raycast.description Run fabric extract_primary_problem on the input text
|
||||
# @raycast.author Daniel Miessler
|
||||
# @raycast.authorURL https://github.com/danielmiessler
|
||||
|
||||
# Set PATH to include common locations and $HOME/go/bin
|
||||
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$HOME/go/bin:$PATH"
|
||||
|
||||
# Use the PATH to find and execute fabric
|
||||
if command -v fabric >/dev/null 2>&1; then
|
||||
fabric -sp extract_primary_problem "${1}"
|
||||
else
|
||||
echo "Error: fabric command not found in PATH"
|
||||
echo "Current PATH: $PATH"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Required parameters:
|
||||
# @raycast.schemaVersion 1
|
||||
# @raycast.title Extract Wisdom
|
||||
# @raycast.mode fullOutput
|
||||
|
||||
# Optional parameters:
|
||||
# @raycast.icon 🧠
|
||||
# @raycast.argument1 { "type": "text", "placeholder": "Input text", "optional": false, "percentEncoded": true}
|
||||
|
||||
# Documentation:
|
||||
# @raycast.description Run fabric extract_wisdom on input text
|
||||
# @raycast.author Daniel Miessler
|
||||
# @raycast.authorURL https://github.com/danielmiessler
|
||||
|
||||
# Set PATH to include common locations and $HOME/go/bin
|
||||
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$HOME/go/bin:$PATH"
|
||||
|
||||
# Use the PATH to find and execute fabric
|
||||
if command -v fabric >/dev/null 2>&1; then
|
||||
fabric -sp extract_wisdom "${1}"
|
||||
else
|
||||
echo "Error: fabric command not found in PATH"
|
||||
echo "Current PATH: $PATH"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Required parameters:
|
||||
# @raycast.schemaVersion 1
|
||||
# @raycast.title Get YouTube Transcript
|
||||
# @raycast.mode fullOutput
|
||||
|
||||
# Optional parameters:
|
||||
# @raycast.icon 🧠
|
||||
# @raycast.argument1 { "type": "text", "placeholder": "Input text", "optional": false, "percentEncoded": false}
|
||||
|
||||
# Documentation:
|
||||
# @raycast.description Run fabric -y on the input text of a YouTube video to get the transcript from.
|
||||
# @raycast.author Daniel Miessler
|
||||
# @raycast.authorURL https://github.com/danielmiessler
|
||||
|
||||
# Set PATH to include common locations and $HOME/go/bin
|
||||
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$HOME/go/bin:$PATH"
|
||||
|
||||
# Use the PATH to find and execute fabric
|
||||
if command -v fabric >/dev/null 2>&1; then
|
||||
fabric -y "${1}"
|
||||
else
|
||||
echo "Error: fabric command not found in PATH"
|
||||
echo "Current PATH: $PATH"
|
||||
exit 1
|
||||
fi
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,25 +0,0 @@
|
||||
# IDENTITY and PURPOSE
|
||||
|
||||
You are a summarization system that extracts the most interesting, useful, and surprising aspects of an article.
|
||||
|
||||
Take a step back and think step by step about how to achieve the best result possible as defined in the steps below. You have a lot of freedom to make this work well.
|
||||
|
||||
## OUTPUT SECTIONS
|
||||
|
||||
1. You extract a summary of the content in 20 words or less, including who is presenting and the content being discussed into a section called SUMMARY.
|
||||
|
||||
2. You extract the top 20 ideas from the input in a section called IDEAS:.
|
||||
|
||||
3. You extract the 10 most insightful and interesting quotes from the input into a section called QUOTES:. Use the exact quote text from the input.
|
||||
|
||||
4. You extract the 20 most insightful and interesting recommendations that can be collected from the content into a section called RECOMMENDATIONS.
|
||||
|
||||
5. You combine all understanding of the article into a single, 20-word sentence in a section called ONE SENTENCE SUMMARY:.
|
||||
|
||||
## OUTPUT INSTRUCTIONS
|
||||
|
||||
1. You only output Markdown.
|
||||
2. Do not give warnings or notes; only output the requested sections.
|
||||
3. You use numbered lists, not bullets.
|
||||
4. Do not repeat ideas, or quotes.
|
||||
5. Do not start items with the same opening words.
|
||||
@@ -1 +0,0 @@
|
||||
CONTENT:
|
||||
115
patterns/summarize_board_meeting/system.md
Normal file
115
patterns/summarize_board_meeting/system.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# IDENTITY AND PURPOSE
|
||||
|
||||
You are a professional meeting secretary specializing in corporate governance documentation. Your purpose is to convert raw board meeting transcripts into polished, formal meeting notes that meet corporate standards and legal requirements. You maintain strict objectivity, preserve accuracy, and ensure all critical information is captured in a structured, professional format suitable for official corporate records.
|
||||
|
||||
# STEPS
|
||||
|
||||
## 1. Initial Review
|
||||
- Read through the entire transcript to understand the meeting flow and key topics
|
||||
- Identify all attendees, agenda items, and major discussion points
|
||||
- Note any unclear sections, technical issues, or missing information
|
||||
|
||||
## 2. Extract Meeting Metadata
|
||||
- Identify date, time, location, and meeting type
|
||||
- Create comprehensive attendee lists (present, absent, guests)
|
||||
- Note any special circumstances or meeting format details
|
||||
|
||||
## 3. Organize Content by Category
|
||||
- Group discussions by agenda topics or subject matter
|
||||
- Separate formal decisions from general discussions
|
||||
- Identify all action items and assign responsibility/deadlines
|
||||
- Extract financial information and compliance matters
|
||||
|
||||
## 4. Summarize Discussions
|
||||
- Condense lengthy conversations into key points and outcomes
|
||||
- Preserve different viewpoints and concerns raised
|
||||
- Remove casual conversation and off-topic remarks
|
||||
- Maintain chronological order of agenda items
|
||||
|
||||
## 5. Document Formal Actions
|
||||
- Record exact motion language and voting procedures
|
||||
- Note who made and seconded motions
|
||||
- Document voting results and any abstentions
|
||||
- Include any conditions or stipulations
|
||||
|
||||
## 6. Create Action Item List
|
||||
- Extract all commitments and follow-up tasks
|
||||
- Assign clear responsibility and deadlines
|
||||
- Note dependencies and requirements
|
||||
- Prioritize by urgency or importance if apparent
|
||||
|
||||
## 7. Quality Review
|
||||
- Verify all names, numbers, and dates are accurate
|
||||
- Ensure professional tone throughout
|
||||
- Check for consistency in terminology
|
||||
- Confirm all major decisions and actions are captured
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- You only output human readable Markdown.
|
||||
- Default to english unless specified otherwise.
|
||||
- Ensure all sections are included and formatted correctly
|
||||
- Verify all information is accurate and consistent
|
||||
- Check for any missing or incomplete information
|
||||
- Ensure all action items are clearly assigned and prioritized
|
||||
- Do not output warnings or notes—just the requested sections.
|
||||
- Do not repeat items in the output sections.
|
||||
|
||||
# OUTPUT SECTIONS
|
||||
|
||||
# Meeting Notes
|
||||
|
||||
## Meeting Details
|
||||
- Date: [Extract from transcript]
|
||||
- Time: [Extract start and end times if available]
|
||||
- Location: [Physical location or virtual platform]
|
||||
- Meeting Type: [Regular Board Meeting/Special Board Meeting/Committee Meeting]
|
||||
|
||||
## Attendees
|
||||
- Present: [List all board members and other attendees who were present]
|
||||
- Absent: [List any noted absences]
|
||||
- Guests: [List any non-board members who attended]
|
||||
|
||||
## Key Agenda Items & Discussions
|
||||
[For each major topic discussed, provide a clear subsection with:]
|
||||
- Topic heading
|
||||
- Brief context or background in 25 words or more
|
||||
- Key points raised during discussion
|
||||
- Different perspectives or concerns mentioned
|
||||
- Any supporting documents referenced
|
||||
|
||||
## Decisions & Resolutions
|
||||
[List all formal decisions made, including:]
|
||||
- Motion text (if formal motions were made)
|
||||
- Who made and seconded motions
|
||||
- Voting results (unanimous, majority, specific vote counts if mentioned)
|
||||
- Any conditions or stipulations attached to decisions
|
||||
|
||||
## Action Items
|
||||
[Create a clear list of follow-up tasks:]
|
||||
- Task description
|
||||
- Assigned person/department
|
||||
- Deadline (if specified)
|
||||
- Any dependencies or requirements
|
||||
|
||||
## Financial Matters
|
||||
[If applicable, summarize:]
|
||||
- Budget discussions
|
||||
- Financial reports presented
|
||||
- Expenditure approvals
|
||||
- Revenue updates
|
||||
|
||||
## Next Steps
|
||||
- Next meeting date and time
|
||||
- Upcoming deadlines
|
||||
- Items to be carried forward
|
||||
|
||||
## Additional Notes
|
||||
- Any conflicts of interest declared
|
||||
- Regulatory or compliance issues discussed
|
||||
- References to policies, bylaws, or legal requirements
|
||||
- Unclear sections or information gaps noted
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/anthropics/anthropic-sdk-go"
|
||||
"github.com/anthropics/anthropic-sdk-go/option"
|
||||
"github.com/danielmiessler/fabric/common"
|
||||
@@ -27,17 +29,19 @@ func NewClient() (ret *Client) {
|
||||
ret.ApiBaseURL = ret.AddSetupQuestion("API Base URL", false)
|
||||
ret.ApiBaseURL.Value = defaultBaseUrl
|
||||
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true)
|
||||
ret.UseWebTool = ret.AddSetupQuestionBool("Web Search Tool Enabled", false)
|
||||
ret.WebToolLocation = ret.AddSetupQuestionCustom("Web Search Tool Location", false,
|
||||
"Enter your approximate timezone location for web search (e.g., 'America/Los_Angeles', see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).")
|
||||
|
||||
ret.maxTokens = 4096
|
||||
ret.defaultRequiredUserMessage = "Hi"
|
||||
ret.models = []string{
|
||||
anthropic.ModelClaude3_7SonnetLatest, anthropic.ModelClaude3_7Sonnet20250219,
|
||||
anthropic.ModelClaude3_5HaikuLatest, anthropic.ModelClaude3_5Haiku20241022,
|
||||
anthropic.ModelClaude3_5SonnetLatest, anthropic.ModelClaude3_5Sonnet20241022,
|
||||
anthropic.ModelClaude_3_5_Sonnet_20240620, anthropic.ModelClaude3OpusLatest,
|
||||
anthropic.ModelClaude_3_Opus_20240229, anthropic.ModelClaude_3_Sonnet_20240229,
|
||||
anthropic.ModelClaude_3_Haiku_20240307, anthropic.ModelClaude_2_1,
|
||||
anthropic.ModelClaude_2_0,
|
||||
string(anthropic.ModelClaude3_7SonnetLatest), string(anthropic.ModelClaude3_7Sonnet20250219),
|
||||
string(anthropic.ModelClaude3_5HaikuLatest), string(anthropic.ModelClaude3_5Haiku20241022),
|
||||
string(anthropic.ModelClaude3_5SonnetLatest), string(anthropic.ModelClaude3_5Sonnet20241022),
|
||||
string(anthropic.ModelClaude_3_5_Sonnet_20240620), string(anthropic.ModelClaude3OpusLatest),
|
||||
string(anthropic.ModelClaude_3_Opus_20240229), string(anthropic.ModelClaude_3_Haiku_20240307),
|
||||
string(anthropic.ModelClaudeOpus4_20250514), string(anthropic.ModelClaudeSonnet4_20250514),
|
||||
}
|
||||
|
||||
return
|
||||
@@ -45,8 +49,10 @@ func NewClient() (ret *Client) {
|
||||
|
||||
type Client struct {
|
||||
*plugins.PluginBase
|
||||
ApiBaseURL *plugins.SetupQuestion
|
||||
ApiKey *plugins.SetupQuestion
|
||||
ApiBaseURL *plugins.SetupQuestion
|
||||
ApiKey *plugins.SetupQuestion
|
||||
UseWebTool *plugins.SetupQuestion
|
||||
WebToolLocation *plugins.SetupQuestion
|
||||
|
||||
maxTokens int
|
||||
defaultRequiredUserMessage string
|
||||
@@ -87,17 +93,12 @@ func (an *Client) SendStream(
|
||||
if len(messages) == 0 {
|
||||
close(channel)
|
||||
// No messages to send after normalization, consider this a non-error condition for streaming.
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
stream := an.client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
|
||||
Model: opts.Model,
|
||||
MaxTokens: int64(an.maxTokens),
|
||||
TopP: anthropic.Opt(opts.TopP),
|
||||
Temperature: anthropic.Opt(opts.Temperature),
|
||||
Messages: messages,
|
||||
})
|
||||
|
||||
stream := an.client.Messages.NewStreaming(ctx, an.buildMessageParams(messages, opts))
|
||||
|
||||
for stream.Next() {
|
||||
event := stream.Current()
|
||||
@@ -115,29 +116,63 @@ func (an *Client) SendStream(
|
||||
return
|
||||
}
|
||||
|
||||
func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
|
||||
messages := an.toMessages(msgs)
|
||||
if len(messages) == 0 {
|
||||
// No messages to send after normalization, return empty string and no error.
|
||||
return "", nil
|
||||
}
|
||||
func (an *Client) buildMessageParams(msgs []anthropic.MessageParam, opts *common.ChatOptions) (
|
||||
params anthropic.MessageNewParams) {
|
||||
|
||||
var message *anthropic.Message
|
||||
if message, err = an.client.Messages.New(ctx, anthropic.MessageNewParams{
|
||||
Model: opts.Model,
|
||||
params = anthropic.MessageNewParams{
|
||||
Model: anthropic.Model(opts.Model),
|
||||
MaxTokens: int64(an.maxTokens),
|
||||
TopP: anthropic.Opt(opts.TopP),
|
||||
Temperature: anthropic.Opt(opts.Temperature),
|
||||
Messages: messages,
|
||||
}); err != nil {
|
||||
Messages: msgs,
|
||||
}
|
||||
|
||||
if plugins.ParseBoolElseFalse(an.UseWebTool.Value) {
|
||||
// Build the web-search tool definition:
|
||||
webTool := anthropic.WebSearchTool20250305Param{
|
||||
Name: "web_search", // string literal instead of constant
|
||||
Type: "web_search_20250305", // string literal instead of constant
|
||||
CacheControl: anthropic.NewCacheControlEphemeralParam(),
|
||||
// Optional: restrict domains or max uses
|
||||
// AllowedDomains: []string{"wikipedia.org", "openai.com"},
|
||||
// MaxUses: anthropic.Opt[int64](5),
|
||||
}
|
||||
|
||||
if an.WebToolLocation.Value != "" {
|
||||
webTool.UserLocation.Type = "approximate"
|
||||
webTool.UserLocation.Timezone = anthropic.Opt(an.WebToolLocation.Value)
|
||||
}
|
||||
|
||||
// Wrap it in the union:
|
||||
params.Tools = []anthropic.ToolUnionParam{
|
||||
{OfWebSearchTool20250305: &webTool},
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (
|
||||
ret string, err error) {
|
||||
|
||||
messages := an.toMessages(msgs)
|
||||
if len(messages) == 0 {
|
||||
// No messages to send after normalization, return empty string and no error.
|
||||
return
|
||||
}
|
||||
|
||||
if len(message.Content) == 0 {
|
||||
// Model returned no content blocks.
|
||||
return "", nil
|
||||
var message *anthropic.Message
|
||||
if message, err = an.client.Messages.New(ctx, an.buildMessageParams(messages, opts)); err != nil {
|
||||
return
|
||||
}
|
||||
ret = message.Content[0].Text
|
||||
|
||||
texts := lo.FilterMap(message.Content, func(block anthropic.ContentBlockUnion, _ int) (ret string, ok bool) {
|
||||
if ok = block.Type == "text" && block.Text != ""; ok {
|
||||
ret = block.Text
|
||||
}
|
||||
return
|
||||
})
|
||||
ret = strings.Join(texts, "")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -206,3 +241,7 @@ func (an *Client) toMessages(msgs []*goopenai.ChatCompletionMessage) (ret []anth
|
||||
|
||||
return anthropicMessages
|
||||
}
|
||||
|
||||
func (an *Client) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -41,3 +41,7 @@ func (oi *Client) ListModels() (ret []string, err error) {
|
||||
ret = oi.apiDeployments
|
||||
return
|
||||
}
|
||||
|
||||
func (oi *Client) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
274
plugins/ai/bedrock/bedrock.go
Normal file
274
plugins/ai/bedrock/bedrock.go
Normal file
@@ -0,0 +1,274 @@
|
||||
// Package bedrock provides a plugin to use Amazon Bedrock models.
|
||||
// Supported models are defined in the MODELS variable.
|
||||
// To add additional models, append them to the MODELS array. Models must support the Converse and ConverseStream operations
|
||||
// Authentication uses the AWS credential provider chain, similar.to the AWS CLI and SDKs
|
||||
// https://docs.aws.amazon.com/sdkref/latest/guide/standardized-credentials.html
|
||||
package bedrock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/danielmiessler/fabric/common"
|
||||
"github.com/danielmiessler/fabric/plugins"
|
||||
"github.com/danielmiessler/fabric/plugins/ai"
|
||||
|
||||
"github.com/aws/aws-sdk-go-v2/aws"
|
||||
"github.com/aws/aws-sdk-go-v2/aws/middleware"
|
||||
"github.com/aws/aws-sdk-go-v2/config"
|
||||
"github.com/aws/aws-sdk-go-v2/service/bedrock"
|
||||
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime"
|
||||
"github.com/aws/aws-sdk-go-v2/service/bedrockruntime/types"
|
||||
|
||||
goopenai "github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
const (
|
||||
userAgentKey = "aiosc"
|
||||
userAgentValue = "fabric"
|
||||
)
|
||||
|
||||
// Ensure BedrockClient implements the ai.Vendor interface
|
||||
var _ ai.Vendor = (*BedrockClient)(nil)
|
||||
|
||||
// BedrockClient is a plugin to add support for Amazon Bedrock.
|
||||
// It implements the plugins.Plugin interface and provides methods
|
||||
// for interacting with AWS Bedrock's Converse and ConverseStream APIs.
|
||||
type BedrockClient struct {
|
||||
*plugins.PluginBase
|
||||
runtimeClient *bedrockruntime.Client
|
||||
controlPlaneClient *bedrock.Client
|
||||
|
||||
bedrockRegion *plugins.SetupQuestion
|
||||
}
|
||||
|
||||
// NewClient returns a new Bedrock plugin client
|
||||
func NewClient() (ret *BedrockClient) {
|
||||
vendorName := "Bedrock"
|
||||
ret = &BedrockClient{}
|
||||
|
||||
ctx := context.Background()
|
||||
cfg, err := config.LoadDefaultConfig(ctx)
|
||||
if err != nil {
|
||||
// Create a minimal client that will fail gracefully during configuration
|
||||
ret.PluginBase = &plugins.PluginBase{
|
||||
Name: vendorName,
|
||||
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
|
||||
ConfigureCustom: func() error {
|
||||
return fmt.Errorf("unable to load AWS Config: %w", err)
|
||||
},
|
||||
}
|
||||
ret.bedrockRegion = ret.PluginBase.AddSetupQuestion("AWS Region", true)
|
||||
return
|
||||
}
|
||||
|
||||
cfg.APIOptions = append(cfg.APIOptions, middleware.AddUserAgentKeyValue(userAgentKey, userAgentValue))
|
||||
|
||||
runtimeClient := bedrockruntime.NewFromConfig(cfg)
|
||||
controlPlaneClient := bedrock.NewFromConfig(cfg)
|
||||
|
||||
ret.PluginBase = &plugins.PluginBase{
|
||||
Name: vendorName,
|
||||
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
|
||||
ConfigureCustom: ret.configure,
|
||||
}
|
||||
|
||||
ret.runtimeClient = runtimeClient
|
||||
ret.controlPlaneClient = controlPlaneClient
|
||||
|
||||
ret.bedrockRegion = ret.PluginBase.AddSetupQuestion("AWS Region", true)
|
||||
|
||||
if cfg.Region != "" {
|
||||
ret.bedrockRegion.Value = cfg.Region
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// isValidAWSRegion validates AWS region format
|
||||
func isValidAWSRegion(region string) bool {
|
||||
// Simple validation - AWS regions are typically 2-3 parts separated by hyphens
|
||||
// Examples: us-east-1, eu-west-1, ap-southeast-2
|
||||
if len(region) < 5 || len(region) > 30 {
|
||||
return false
|
||||
}
|
||||
// Basic pattern check for AWS region format
|
||||
return region != ""
|
||||
}
|
||||
|
||||
// configure initializes the Bedrock clients with the specified AWS region.
|
||||
// If no region is specified, the default region from AWS config is used.
|
||||
func (c *BedrockClient) configure() error {
|
||||
if c.bedrockRegion.Value == "" {
|
||||
return nil // Use default region from AWS config
|
||||
}
|
||||
|
||||
// Validate region format
|
||||
if !isValidAWSRegion(c.bedrockRegion.Value) {
|
||||
return fmt.Errorf("invalid AWS region: %s", c.bedrockRegion.Value)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
cfg, err := config.LoadDefaultConfig(ctx, config.WithRegion(c.bedrockRegion.Value))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load AWS Config with region %s: %w", c.bedrockRegion.Value, err)
|
||||
}
|
||||
|
||||
cfg.APIOptions = append(cfg.APIOptions, middleware.AddUserAgentKeyValue(userAgentKey, userAgentValue))
|
||||
|
||||
c.runtimeClient = bedrockruntime.NewFromConfig(cfg)
|
||||
c.controlPlaneClient = bedrock.NewFromConfig(cfg)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListModels retrieves all available foundation models and inference profiles
|
||||
// from AWS Bedrock that can be used with this plugin.
|
||||
func (c *BedrockClient) ListModels() ([]string, error) {
|
||||
models := []string{}
|
||||
ctx := context.Background()
|
||||
|
||||
foundationModels, err := c.controlPlaneClient.ListFoundationModels(ctx, &bedrock.ListFoundationModelsInput{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list foundation models: %w", err)
|
||||
}
|
||||
|
||||
for _, model := range foundationModels.ModelSummaries {
|
||||
models = append(models, *model.ModelId)
|
||||
}
|
||||
|
||||
inferenceProfilesPaginator := bedrock.NewListInferenceProfilesPaginator(c.controlPlaneClient, &bedrock.ListInferenceProfilesInput{})
|
||||
|
||||
for inferenceProfilesPaginator.HasMorePages() {
|
||||
inferenceProfiles, err := inferenceProfilesPaginator.NextPage(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to list inference profiles: %w", err)
|
||||
}
|
||||
|
||||
for _, profile := range inferenceProfiles.InferenceProfileSummaries {
|
||||
models = append(models, *profile.InferenceProfileId)
|
||||
}
|
||||
}
|
||||
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// SendStream sends the messages to the the Bedrock ConverseStream API
|
||||
func (c *BedrockClient) SendStream(msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string) (err error) {
|
||||
// Ensure channel is closed on all exit paths to prevent goroutine leaks
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = fmt.Errorf("panic in SendStream: %v", r)
|
||||
}
|
||||
close(channel)
|
||||
}()
|
||||
|
||||
messages := c.toMessages(msgs)
|
||||
|
||||
var converseInput = bedrockruntime.ConverseStreamInput{
|
||||
ModelId: aws.String(opts.Model),
|
||||
Messages: messages,
|
||||
InferenceConfig: &types.InferenceConfiguration{
|
||||
Temperature: aws.Float32(float32(opts.Temperature)),
|
||||
TopP: aws.Float32(float32(opts.TopP))},
|
||||
}
|
||||
|
||||
response, err := c.runtimeClient.ConverseStream(context.Background(), &converseInput)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bedrock conversestream failed for model %s: %w", opts.Model, err)
|
||||
}
|
||||
|
||||
for event := range response.GetStream().Events() {
|
||||
// Possible ConverseStream event types
|
||||
// https://docs.aws.amazon.com/bedrock/latest/userguide/conversation-inference-call.html#conversation-inference-call-response-converse-stream
|
||||
switch v := event.(type) {
|
||||
|
||||
case *types.ConverseStreamOutputMemberContentBlockDelta:
|
||||
text, ok := v.Value.Delta.(*types.ContentBlockDeltaMemberText)
|
||||
if ok {
|
||||
channel <- text.Value
|
||||
}
|
||||
|
||||
case *types.ConverseStreamOutputMemberMessageStop:
|
||||
channel <- "\n"
|
||||
return nil // Let defer handle the close
|
||||
|
||||
// Unused Events
|
||||
case *types.ConverseStreamOutputMemberMessageStart,
|
||||
*types.ConverseStreamOutputMemberContentBlockStart,
|
||||
*types.ConverseStreamOutputMemberContentBlockStop,
|
||||
*types.ConverseStreamOutputMemberMetadata:
|
||||
|
||||
default:
|
||||
return fmt.Errorf("unknown stream event type: %T", v)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Send sends the messages the Bedrock Converse API
|
||||
func (c *BedrockClient) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (ret string, err error) {
|
||||
|
||||
messages := c.toMessages(msgs)
|
||||
|
||||
var converseInput = bedrockruntime.ConverseInput{
|
||||
ModelId: aws.String(opts.Model),
|
||||
Messages: messages,
|
||||
}
|
||||
response, err := c.runtimeClient.Converse(ctx, &converseInput)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("bedrock converse failed for model %s: %w", opts.Model, err)
|
||||
}
|
||||
|
||||
responseText, ok := response.Output.(*types.ConverseOutputMemberMessage)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unexpected response type: %T", response.Output)
|
||||
}
|
||||
|
||||
if len(responseText.Value.Content) == 0 {
|
||||
return "", fmt.Errorf("empty response content")
|
||||
}
|
||||
|
||||
responseContentBlock := responseText.Value.Content[0]
|
||||
text, ok := responseContentBlock.(*types.ContentBlockMemberText)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("unexpected content block type: %T", responseContentBlock)
|
||||
}
|
||||
|
||||
return text.Value, nil
|
||||
}
|
||||
|
||||
// NeedsRawMode indicates whether the model requires raw mode processing.
|
||||
// Bedrock models do not require raw mode.
|
||||
func (c *BedrockClient) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// toMessages converts the array of input messages from the ChatCompletionMessageType to the
|
||||
// Bedrock Converse Message type.
|
||||
// The system role messages are mapped to the user role as they contain a mix of system messages,
|
||||
// pattern content and user input.
|
||||
func (c *BedrockClient) toMessages(inputMessages []*goopenai.ChatCompletionMessage) (messages []types.Message) {
|
||||
for _, msg := range inputMessages {
|
||||
roles := map[string]types.ConversationRole{
|
||||
goopenai.ChatMessageRoleUser: types.ConversationRoleUser,
|
||||
goopenai.ChatMessageRoleAssistant: types.ConversationRoleAssistant,
|
||||
goopenai.ChatMessageRoleSystem: types.ConversationRoleUser,
|
||||
}
|
||||
|
||||
role, ok := roles[msg.Role]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
message := types.Message{
|
||||
Role: role,
|
||||
Content: []types.ContentBlock{&types.ContentBlockMemberText{Value: msg.Content}},
|
||||
}
|
||||
messages = append(messages, message)
|
||||
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -90,3 +90,7 @@ func (c *Client) Setup() error {
|
||||
func (c *Client) SetupFillEnvFileContent(_ *bytes.Buffer) {
|
||||
// No environment variables needed for dry run
|
||||
}
|
||||
|
||||
func (c *Client) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -43,3 +43,7 @@ func (oi *Client) ListModels() (ret []string, err error) {
|
||||
ret = oi.apiModels
|
||||
return
|
||||
}
|
||||
|
||||
func (oi *Client) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -143,6 +143,10 @@ func (o *Client) extractText(response *genai.GenerateContentResponse) (ret strin
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func toMessages(msgs []*goopenai.ChatCompletionMessage) (systemInstruction *genai.Content, messages []genai.Part) {
|
||||
if len(msgs) >= 2 {
|
||||
systemInstruction = &genai.Content{
|
||||
|
||||
@@ -345,3 +345,7 @@ func (c *Client) GetEmbeddings(ctx context.Context, input string, opts *common.C
|
||||
embeddings = result.Data[0].Embedding
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) NeedsRawMode(modelName string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
ollamaapi "github.com/ollama/ollama/api"
|
||||
@@ -32,16 +33,20 @@ func NewClient() (ret *Client) {
|
||||
ret.ApiUrl.Value = defaultBaseUrl
|
||||
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", false)
|
||||
ret.ApiKey.Value = ""
|
||||
ret.ApiHttpTimeout = ret.AddSetupQuestionCustom("HTTP Timeout", true,
|
||||
"Specify HTTP timeout duration for Ollama requests (e.g. 30s, 5m, 1h)")
|
||||
ret.ApiHttpTimeout.Value = "20m"
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
*plugins.PluginBase
|
||||
ApiUrl *plugins.SetupQuestion
|
||||
ApiKey *plugins.SetupQuestion
|
||||
apiUrl *url.URL
|
||||
client *ollamaapi.Client
|
||||
ApiUrl *plugins.SetupQuestion
|
||||
ApiKey *plugins.SetupQuestion
|
||||
apiUrl *url.URL
|
||||
client *ollamaapi.Client
|
||||
ApiHttpTimeout *plugins.SetupQuestion
|
||||
}
|
||||
|
||||
type transport_sec struct {
|
||||
@@ -62,7 +67,19 @@ func (o *Client) configure() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
o.client = ollamaapi.NewClient(o.apiUrl, &http.Client{Timeout: 1200000 * time.Millisecond, Transport: &transport_sec{underlyingTransport: http.DefaultTransport, ApiKey: o.ApiKey}})
|
||||
timeout := 20 * time.Minute // Default timeout
|
||||
|
||||
if o.ApiHttpTimeout != nil {
|
||||
parsed, err := time.ParseDuration(o.ApiHttpTimeout.Value)
|
||||
if err == nil && o.ApiHttpTimeout.Value != "" {
|
||||
timeout = parsed
|
||||
} else if o.ApiHttpTimeout.Value != "" {
|
||||
fmt.Printf("Invalid HTTP timeout format (%q), using default (20m): %v\n", o.ApiHttpTimeout.Value, err)
|
||||
}
|
||||
}
|
||||
|
||||
o.client = ollamaapi.NewClient(o.apiUrl, &http.Client{Timeout: timeout, Transport: &transport_sec{underlyingTransport: http.DefaultTransport, ApiKey: o.ApiKey}})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -138,3 +155,16 @@ func (o *Client) createChatRequest(msgs []*goopenai.ChatCompletionMessage, opts
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) NeedsRawMode(modelName string) bool {
|
||||
ollamaPrefixes := []string{
|
||||
"llama3",
|
||||
"llama2",
|
||||
}
|
||||
for _, prefix := range ollamaPrefixes {
|
||||
if strings.HasPrefix(modelName, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -123,6 +123,20 @@ func (o *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessag
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) NeedsRawMode(modelName string) bool {
|
||||
openaiModelsPrefixes := []string{
|
||||
"o1",
|
||||
"o3",
|
||||
"o4",
|
||||
}
|
||||
for _, prefix := range openaiModelsPrefixes {
|
||||
if strings.HasPrefix(modelName, prefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *Client) buildChatCompletionRequest(
|
||||
inputMsgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions,
|
||||
) (ret goopenai.ChatCompletionRequest) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package openai_compatible
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/plugins/ai/openai"
|
||||
)
|
||||
|
||||
@@ -24,29 +27,37 @@ func NewClient(providerConfig ProviderConfig) *Client {
|
||||
|
||||
// ProviderMap is a map of provider name to ProviderConfig for O(1) lookup
|
||||
var ProviderMap = map[string]ProviderConfig{
|
||||
"Mistral": {
|
||||
Name: "Mistral",
|
||||
BaseURL: "https://api.mistral.ai/v1",
|
||||
"AIML": {
|
||||
Name: "AIML",
|
||||
BaseURL: "https://api.aimlapi.com/v1",
|
||||
},
|
||||
"LiteLLM": {
|
||||
Name: "LiteLLM",
|
||||
BaseURL: "http://localhost:4000",
|
||||
},
|
||||
"Groq": {
|
||||
Name: "Groq",
|
||||
BaseURL: "https://api.groq.com/openai/v1",
|
||||
},
|
||||
"GrokAI": {
|
||||
Name: "GrokAI",
|
||||
BaseURL: "https://api.x.ai/v1",
|
||||
"Cerebras": {
|
||||
Name: "Cerebras",
|
||||
BaseURL: "https://api.cerebras.ai/v1",
|
||||
},
|
||||
"DeepSeek": {
|
||||
Name: "DeepSeek",
|
||||
BaseURL: "https://api.deepseek.com",
|
||||
},
|
||||
"Cerebras": {
|
||||
Name: "Cerebras",
|
||||
BaseURL: "https://api.cerebras.ai/v1",
|
||||
"GrokAI": {
|
||||
Name: "GrokAI",
|
||||
BaseURL: "https://api.x.ai/v1",
|
||||
},
|
||||
"Groq": {
|
||||
Name: "Groq",
|
||||
BaseURL: "https://api.groq.com/openai/v1",
|
||||
},
|
||||
"Langdock": {
|
||||
Name: "Langdock",
|
||||
BaseURL: "https://api.langdock.com/openai/{{REGION=us}}/v1",
|
||||
},
|
||||
"LiteLLM": {
|
||||
Name: "LiteLLM",
|
||||
BaseURL: "http://localhost:4000",
|
||||
},
|
||||
"Mistral": {
|
||||
Name: "Mistral",
|
||||
BaseURL: "https://api.mistral.ai/v1",
|
||||
},
|
||||
"OpenRouter": {
|
||||
Name: "OpenRouter",
|
||||
@@ -61,6 +72,32 @@ var ProviderMap = map[string]ProviderConfig{
|
||||
// GetProviderByName returns the provider configuration for a given name with O(1) lookup
|
||||
func GetProviderByName(name string) (ProviderConfig, bool) {
|
||||
provider, found := ProviderMap[name]
|
||||
if strings.Contains(provider.BaseURL, "{{") && strings.Contains(provider.BaseURL, "}}") {
|
||||
// Extract the template variable and default value
|
||||
start := strings.Index(provider.BaseURL, "{{")
|
||||
end := strings.Index(provider.BaseURL, "}}") + 2
|
||||
template := provider.BaseURL[start:end]
|
||||
|
||||
// Parse the template to get variable name and default value
|
||||
inner := template[2 : len(template)-2] // Remove {{ and }}
|
||||
parts := strings.Split(inner, "=")
|
||||
if len(parts) == 2 {
|
||||
varName := strings.TrimSpace(parts[0])
|
||||
defaultValue := strings.TrimSpace(parts[1])
|
||||
|
||||
// Create environment variable name
|
||||
envVarName := strings.ToUpper(provider.Name) + "_" + varName
|
||||
|
||||
// Get value from environment or use default
|
||||
envValue := os.Getenv(envVarName)
|
||||
if envValue == "" {
|
||||
envValue = defaultValue
|
||||
}
|
||||
|
||||
// Replace the template with the actual value
|
||||
provider.BaseURL = strings.Replace(provider.BaseURL, template, envValue, 1)
|
||||
}
|
||||
}
|
||||
return provider, found
|
||||
}
|
||||
|
||||
|
||||
246
plugins/ai/perplexity/perplexity.go
Normal file
246
plugins/ai/perplexity/perplexity.go
Normal file
@@ -0,0 +1,246 @@
|
||||
package perplexity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync" // Added sync package
|
||||
|
||||
"github.com/danielmiessler/fabric/common"
|
||||
"github.com/danielmiessler/fabric/plugins"
|
||||
perplexity "github.com/sgaunet/perplexity-go/v2"
|
||||
|
||||
goopenai "github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
const (
|
||||
providerName = "Perplexity"
|
||||
)
|
||||
|
||||
var models = []string{
|
||||
"r1-1776", "sonar", "sonar-pro", "sonar-reasoning", "sonar-reasoning-pro",
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
*plugins.PluginBase
|
||||
APIKey *plugins.SetupQuestion
|
||||
client *perplexity.Client
|
||||
}
|
||||
|
||||
func NewClient() *Client {
|
||||
c := &Client{}
|
||||
c.PluginBase = &plugins.PluginBase{
|
||||
Name: providerName,
|
||||
EnvNamePrefix: plugins.BuildEnvVariablePrefix(providerName),
|
||||
ConfigureCustom: c.Configure, // Assign the Configure method
|
||||
}
|
||||
c.APIKey = c.AddSetupQuestion("API_KEY", true)
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Client) Configure() error {
|
||||
// The PluginBase.Configure() is called by the framework if needed.
|
||||
// We only need to handle specific logic for this plugin.
|
||||
if c.APIKey.Value == "" {
|
||||
// Attempt to get from environment variable if not set by user during setup
|
||||
envKey := c.EnvNamePrefix + "API_KEY"
|
||||
apiKeyFromEnv := os.Getenv(envKey)
|
||||
if apiKeyFromEnv != "" {
|
||||
c.APIKey.Value = apiKeyFromEnv
|
||||
} else {
|
||||
return fmt.Errorf("%s API key not configured. Please set the %s environment variable or run 'fabric --setup %s'", providerName, envKey, providerName)
|
||||
}
|
||||
}
|
||||
c.client = perplexity.NewClient(c.APIKey.Value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
// Perplexity API does not have a ListModels endpoint.
|
||||
// We return a predefined list.
|
||||
return models, nil
|
||||
}
|
||||
|
||||
func (c *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions) (string, error) {
|
||||
if c.client == nil {
|
||||
if err := c.Configure(); err != nil {
|
||||
return "", fmt.Errorf("failed to configure Perplexity client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var perplexityMessages []perplexity.Message
|
||||
for _, msg := range msgs {
|
||||
perplexityMessages = append(perplexityMessages, perplexity.Message{
|
||||
Role: msg.Role,
|
||||
Content: msg.Content,
|
||||
})
|
||||
}
|
||||
|
||||
requestOptions := []perplexity.CompletionRequestOption{
|
||||
perplexity.WithModel(opts.Model),
|
||||
perplexity.WithMessages(perplexityMessages),
|
||||
}
|
||||
if opts.MaxTokens > 0 {
|
||||
requestOptions = append(requestOptions, perplexity.WithMaxTokens(opts.MaxTokens))
|
||||
}
|
||||
if opts.Temperature > 0 { // Perplexity default is 1.0, only set if user specifies
|
||||
requestOptions = append(requestOptions, perplexity.WithTemperature(opts.Temperature))
|
||||
}
|
||||
if opts.TopP > 0 { // Perplexity default is not specified, typically 1.0
|
||||
requestOptions = append(requestOptions, perplexity.WithTopP(opts.TopP))
|
||||
}
|
||||
if opts.PresencePenalty != 0 {
|
||||
// Corrected: Pass float64 directly
|
||||
requestOptions = append(requestOptions, perplexity.WithPresencePenalty(opts.PresencePenalty))
|
||||
}
|
||||
if opts.FrequencyPenalty != 0 {
|
||||
// Corrected: Pass float64 directly
|
||||
requestOptions = append(requestOptions, perplexity.WithFrequencyPenalty(opts.FrequencyPenalty))
|
||||
}
|
||||
|
||||
request := perplexity.NewCompletionRequest(requestOptions...)
|
||||
|
||||
// Corrected: Use SendCompletionRequest method from perplexity-go library
|
||||
resp, err := c.client.SendCompletionRequest(request) // Pass request directly
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("perplexity API request failed: %w", err) // Corrected capitalization
|
||||
}
|
||||
|
||||
content := resp.GetLastContent()
|
||||
|
||||
// Append citations if available
|
||||
citations := resp.GetCitations()
|
||||
if len(citations) > 0 {
|
||||
content += "\n\n# CITATIONS\n\n"
|
||||
for i, citation := range citations {
|
||||
content += fmt.Sprintf("- [%d] %s\n", i+1, citation)
|
||||
}
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func (c *Client) SendStream(msgs []*goopenai.ChatCompletionMessage, opts *common.ChatOptions, channel chan string) error {
|
||||
if c.client == nil {
|
||||
if err := c.Configure(); err != nil {
|
||||
close(channel) // Ensure channel is closed on error
|
||||
return fmt.Errorf("failed to configure Perplexity client: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
var perplexityMessages []perplexity.Message
|
||||
for _, msg := range msgs {
|
||||
perplexityMessages = append(perplexityMessages, perplexity.Message{
|
||||
Role: msg.Role,
|
||||
Content: msg.Content,
|
||||
})
|
||||
}
|
||||
|
||||
requestOptions := []perplexity.CompletionRequestOption{
|
||||
perplexity.WithModel(opts.Model),
|
||||
perplexity.WithMessages(perplexityMessages),
|
||||
perplexity.WithStream(true), // Enable streaming
|
||||
}
|
||||
|
||||
if opts.MaxTokens > 0 {
|
||||
requestOptions = append(requestOptions, perplexity.WithMaxTokens(opts.MaxTokens))
|
||||
}
|
||||
if opts.Temperature > 0 {
|
||||
requestOptions = append(requestOptions, perplexity.WithTemperature(opts.Temperature))
|
||||
}
|
||||
if opts.TopP > 0 {
|
||||
requestOptions = append(requestOptions, perplexity.WithTopP(opts.TopP))
|
||||
}
|
||||
if opts.PresencePenalty != 0 {
|
||||
// Corrected: Pass float64 directly
|
||||
requestOptions = append(requestOptions, perplexity.WithPresencePenalty(opts.PresencePenalty))
|
||||
}
|
||||
if opts.FrequencyPenalty != 0 {
|
||||
// Corrected: Pass float64 directly
|
||||
requestOptions = append(requestOptions, perplexity.WithFrequencyPenalty(opts.FrequencyPenalty))
|
||||
}
|
||||
|
||||
request := perplexity.NewCompletionRequest(requestOptions...)
|
||||
|
||||
responseChan := make(chan perplexity.CompletionResponse)
|
||||
var wg sync.WaitGroup // Use sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
go func() {
|
||||
err := c.client.SendSSEHTTPRequest(&wg, request, responseChan)
|
||||
if err != nil {
|
||||
// Log error, can't send to string channel directly.
|
||||
// Consider a mechanism to propagate this error if needed.
|
||||
fmt.Fprintf(os.Stderr, "perplexity streaming error: %v\\n", err) // Corrected capitalization
|
||||
// If the error occurs during stream setup, the channel might not have been closed by the receiver loop.
|
||||
// However, closing it here might cause a panic if the receiver loop also tries to close it.
|
||||
// close(channel) // Caution: Uncommenting this may cause panic, as channel is closed in the receiver goroutine.
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer close(channel) // Ensure the output channel is closed when this goroutine finishes
|
||||
var lastResponse *perplexity.CompletionResponse
|
||||
for resp := range responseChan {
|
||||
lastResponse = &resp
|
||||
if len(resp.Choices) > 0 {
|
||||
content := ""
|
||||
// Corrected: Check Delta.Content and Message.Content directly for non-emptiness
|
||||
// as Delta and Message are structs, not pointers, in perplexity.Choice
|
||||
if resp.Choices[0].Delta.Content != "" {
|
||||
content = resp.Choices[0].Delta.Content
|
||||
} else if resp.Choices[0].Message.Content != "" {
|
||||
content = resp.Choices[0].Message.Content
|
||||
}
|
||||
if content != "" {
|
||||
channel <- content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send citations at the end if available
|
||||
if lastResponse != nil {
|
||||
citations := lastResponse.GetCitations()
|
||||
if len(citations) > 0 {
|
||||
channel <- "\n\n# CITATIONS\n\n"
|
||||
for i, citation := range citations {
|
||||
channel <- fmt.Sprintf("- [%d] %s\n", i+1, citation)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) NeedsRawMode(modelName string) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Setup is called by the fabric CLI framework to guide the user through configuration.
|
||||
func (c *Client) Setup() error {
|
||||
return c.PluginBase.Setup()
|
||||
}
|
||||
|
||||
// GetName returns the name of the plugin.
|
||||
func (c *Client) GetName() string {
|
||||
return c.PluginBase.Name
|
||||
}
|
||||
|
||||
// GetEnvNamePrefix returns the environment variable prefix for the plugin.
|
||||
// Corrected: Receiver name
|
||||
func (c *Client) GetEnvNamePrefix() string {
|
||||
return c.PluginBase.EnvNamePrefix
|
||||
}
|
||||
|
||||
// AddSetupQuestion adds a setup question to the plugin.
|
||||
// This is a helper method, usually called from NewClient.
|
||||
func (c *Client) AddSetupQuestion(text string, isSensitive bool) *plugins.SetupQuestion {
|
||||
return c.PluginBase.AddSetupQuestion(text, isSensitive)
|
||||
}
|
||||
|
||||
// GetSetupQuestions returns the setup questions for the plugin.
|
||||
// Corrected: Return the slice of setup questions from PluginBase
|
||||
func (c *Client) GetSetupQuestions() []*plugins.SetupQuestion {
|
||||
return c.PluginBase.SetupQuestions
|
||||
}
|
||||
@@ -14,4 +14,5 @@ type Vendor interface {
|
||||
ListModels() ([]string, error)
|
||||
SendStream([]*goopenai.ChatCompletionMessage, *common.ChatOptions, chan string) error
|
||||
Send(context.Context, []*goopenai.ChatCompletionMessage, *common.ChatOptions) (string, error)
|
||||
NeedsRawMode(modelName string) bool
|
||||
}
|
||||
|
||||
@@ -150,3 +150,14 @@ func (o *PatternsEntity) Get(name string) (*Pattern, error) {
|
||||
// Use GetPattern with no variables
|
||||
return o.GetApplyVariables(name, nil, "")
|
||||
}
|
||||
func (o *PatternsEntity) Save(name string, content []byte) (err error) {
|
||||
patternDir := filepath.Join(o.Dir, name)
|
||||
if err = os.MkdirAll(patternDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("could not create pattern directory: %v", err)
|
||||
}
|
||||
patternPath := filepath.Join(patternDir, o.SystemPatternFile)
|
||||
if err = os.WriteFile(patternPath, content, 0644); err != nil {
|
||||
return fmt.Errorf("could not save pattern: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -144,3 +144,21 @@ func TestGetApplyVariables(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatternsEntity_Save(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
name := "new-pattern"
|
||||
content := []byte("test pattern content")
|
||||
require.NoError(t, entity.Save(name, content))
|
||||
|
||||
patternDir := filepath.Join(entity.Dir, name)
|
||||
info, err := os.Stat(patternDir)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, info.IsDir())
|
||||
|
||||
data, err := os.ReadFile(filepath.Join(patternDir, entity.SystemPatternFile))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, content, data)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
const AnswerReset = "reset"
|
||||
const SettingTypeBool = "bool"
|
||||
|
||||
type Plugin interface {
|
||||
GetName() string
|
||||
@@ -60,6 +61,21 @@ func (o *PluginBase) AddSetupQuestionCustom(name string, required bool, question
|
||||
return
|
||||
}
|
||||
|
||||
func (o *PluginBase) AddSetupQuestionBool(name string, required bool) (ret *SetupQuestion) {
|
||||
return o.AddSetupQuestionCustomBool(name, required, "")
|
||||
}
|
||||
|
||||
func (o *PluginBase) AddSetupQuestionCustomBool(name string, required bool, question string) (ret *SetupQuestion) {
|
||||
setting := o.AddSetting(name, required)
|
||||
setting.Type = SettingTypeBool
|
||||
ret = &SetupQuestion{Setting: setting, Question: question}
|
||||
if ret.Question == "" {
|
||||
ret.Question = fmt.Sprintf("Enable %v %v (true/false)", o.Name, strings.ToUpper(name))
|
||||
}
|
||||
o.SetupQuestions = append(o.SetupQuestions, ret)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *PluginBase) Configure() (err error) {
|
||||
if err = o.Settings.Configure(); err != nil {
|
||||
return
|
||||
@@ -98,16 +114,123 @@ func NewSetting(envVariable string, required bool) *Setting {
|
||||
}
|
||||
}
|
||||
|
||||
// In plugins/plugin.go
|
||||
|
||||
type Setting struct {
|
||||
EnvVariable string
|
||||
Value string
|
||||
Required bool
|
||||
Type string // "string" (default), "bool"
|
||||
}
|
||||
|
||||
func (o *Setting) IsValid() bool {
|
||||
if o.Type == SettingTypeBool {
|
||||
_, err := ParseBool(o.Value)
|
||||
return (err == nil) || !o.Required
|
||||
}
|
||||
return o.IsDefined() || !o.Required
|
||||
}
|
||||
|
||||
func (o *Setting) Print() {
|
||||
if o.Type == SettingTypeBool {
|
||||
v, _ := ParseBool(o.Value)
|
||||
fmt.Printf("%v: %v\n", o.EnvVariable, v)
|
||||
} else {
|
||||
fmt.Printf("%v: %v\n", o.EnvVariable, o.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *Setting) FillEnvFileContent(buffer *bytes.Buffer) {
|
||||
if o.IsDefined() {
|
||||
buffer.WriteString(o.EnvVariable)
|
||||
buffer.WriteString("=")
|
||||
if o.Type == SettingTypeBool {
|
||||
v, _ := ParseBool(o.Value)
|
||||
buffer.WriteString(fmt.Sprintf("%v", v))
|
||||
} else {
|
||||
buffer.WriteString(o.Value)
|
||||
}
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func ParseBoolElseFalse(val string) (ret bool) {
|
||||
ret, _ = ParseBool(val)
|
||||
return
|
||||
}
|
||||
|
||||
func ParseBool(val string) (bool, error) {
|
||||
switch strings.ToLower(strings.TrimSpace(val)) {
|
||||
case "1", "true", "yes", "on":
|
||||
return true, nil
|
||||
case "0", "false", "no", "off":
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf("invalid bool: %q", val)
|
||||
}
|
||||
|
||||
type SetupQuestion struct {
|
||||
*Setting
|
||||
Question string
|
||||
}
|
||||
|
||||
func (o *SetupQuestion) Ask(label string) (err error) {
|
||||
var prefix string
|
||||
if label != "" {
|
||||
prefix = fmt.Sprintf("[%v] ", label)
|
||||
} else {
|
||||
prefix = ""
|
||||
}
|
||||
fmt.Println()
|
||||
if o.Type == SettingTypeBool {
|
||||
current := "false"
|
||||
if v, err := ParseBool(o.Value); err == nil && v {
|
||||
current = "true"
|
||||
}
|
||||
fmt.Printf("%v%v (true/false, leave empty for '%s' or type '%v' to remove the value):\n",
|
||||
prefix, o.Question, current, AnswerReset)
|
||||
} else if o.Value != "" {
|
||||
fmt.Printf("%v%v (leave empty for '%s' or type '%v' to remove the value):\n",
|
||||
prefix, o.Question, o.Value, AnswerReset)
|
||||
} else {
|
||||
fmt.Printf("%v%v (leave empty to skip):\n", prefix, o.Question)
|
||||
}
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
answer = strings.TrimRight(answer, "\n")
|
||||
if answer == "" {
|
||||
answer = o.Value
|
||||
} else if strings.ToLower(answer) == AnswerReset {
|
||||
answer = ""
|
||||
}
|
||||
err = o.OnAnswer(answer)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *SetupQuestion) OnAnswer(answer string) (err error) {
|
||||
if o.Type == SettingTypeBool {
|
||||
if answer == "" {
|
||||
o.Value = ""
|
||||
} else {
|
||||
_, err := ParseBool(answer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid boolean value: %v", answer)
|
||||
}
|
||||
o.Value = strings.ToLower(answer)
|
||||
}
|
||||
} else {
|
||||
o.Value = answer
|
||||
}
|
||||
if o.EnvVariable != "" {
|
||||
if err = os.Setenv(o.EnvVariable, o.Value); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = o.IsValidErr()
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Setting) IsValidErr() (err error) {
|
||||
if !o.IsValid() {
|
||||
err = fmt.Errorf("%v=%v, is not valid", o.EnvVariable, o.Value)
|
||||
@@ -127,71 +250,10 @@ func (o *Setting) Configure() error {
|
||||
return o.IsValidErr()
|
||||
}
|
||||
|
||||
func (o *Setting) FillEnvFileContent(buffer *bytes.Buffer) {
|
||||
if o.IsDefined() {
|
||||
buffer.WriteString(o.EnvVariable)
|
||||
buffer.WriteString("=")
|
||||
//buffer.WriteString("\"")
|
||||
buffer.WriteString(o.Value)
|
||||
//buffer.WriteString("\"")
|
||||
buffer.WriteString("\n")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Setting) Print() {
|
||||
fmt.Printf("%v: %v\n", o.EnvVariable, o.Value)
|
||||
}
|
||||
|
||||
func NewSetupQuestion(question string) *SetupQuestion {
|
||||
return &SetupQuestion{Setting: &Setting{}, Question: question}
|
||||
}
|
||||
|
||||
type SetupQuestion struct {
|
||||
*Setting
|
||||
Question string
|
||||
}
|
||||
|
||||
func (o *SetupQuestion) Ask(label string) (err error) {
|
||||
var prefix string
|
||||
|
||||
if label != "" {
|
||||
prefix = fmt.Sprintf("[%v] ", label)
|
||||
} else {
|
||||
prefix = ""
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
if o.Value != "" {
|
||||
fmt.Printf("%v%v (leave empty for '%s' or type '%v' to remove the value):\n",
|
||||
prefix, o.Question, o.Value, AnswerReset)
|
||||
} else {
|
||||
fmt.Printf("%v%v (leave empty to skip):\n", prefix, o.Question)
|
||||
}
|
||||
|
||||
var answer string
|
||||
fmt.Scanln(&answer)
|
||||
answer = strings.TrimRight(answer, "\n")
|
||||
if answer == "" {
|
||||
answer = o.Value
|
||||
} else if strings.ToLower(answer) == AnswerReset {
|
||||
answer = ""
|
||||
}
|
||||
err = o.OnAnswer(answer)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *SetupQuestion) OnAnswer(answer string) (err error) {
|
||||
o.Value = answer
|
||||
if o.EnvVariable != "" {
|
||||
if err = os.Setenv(o.EnvVariable, answer); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
err = o.IsValidErr()
|
||||
return
|
||||
}
|
||||
|
||||
type Settings []*Setting
|
||||
|
||||
func (o Settings) IsConfigured() (ret bool) {
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
// Package youtube provides YouTube video transcript and comment extraction functionality.
|
||||
//
|
||||
// Requirements:
|
||||
// - yt-dlp: Required for transcript extraction (must be installed separately)
|
||||
// - YouTube API key: Optional, only needed for comments and metadata extraction
|
||||
//
|
||||
// The implementation uses yt-dlp for reliable transcript extraction and the YouTube API
|
||||
// for comments/metadata. Old YouTube scraping methods have been removed due to
|
||||
// frequent changes and rate limiting.
|
||||
package youtube
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/anaskhan96/soup"
|
||||
"github.com/danielmiessler/fabric/plugins"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/youtube/v3"
|
||||
@@ -27,7 +36,7 @@ func NewYouTube() (ret *YouTube) {
|
||||
|
||||
ret.PluginBase = &plugins.PluginBase{
|
||||
Name: label,
|
||||
SetupDescription: label + " - to grab video transcripts and comments",
|
||||
SetupDescription: label + " - to grab video transcripts (via yt-dlp) and comments/metadata (via YouTube API)",
|
||||
EnvNamePrefix: plugins.BuildEnvVariablePrefix(label),
|
||||
}
|
||||
|
||||
@@ -46,6 +55,10 @@ type YouTube struct {
|
||||
|
||||
func (o *YouTube) initService() (err error) {
|
||||
if o.service == nil {
|
||||
if o.ApiKey.Value == "" {
|
||||
err = fmt.Errorf("YouTube API key required for comments and metadata. Run 'fabric --setup' to configure")
|
||||
return
|
||||
}
|
||||
o.normalizeRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`)
|
||||
ctx := context.Background()
|
||||
o.service, err = youtube.NewService(ctx, option.WithAPIKey(o.ApiKey.Value))
|
||||
@@ -54,10 +67,6 @@ func (o *YouTube) initService() (err error) {
|
||||
}
|
||||
|
||||
func (o *YouTube) GetVideoOrPlaylistId(url string) (videoId string, playlistId string, err error) {
|
||||
if err = o.initService(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Video ID pattern
|
||||
videoPattern := `(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/(?:live\/|[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|(?:s(?:horts)\/)|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]*)`
|
||||
videoRe := regexp.MustCompile(videoPattern)
|
||||
@@ -94,112 +103,182 @@ func (o *YouTube) GrabTranscriptForUrl(url string, language string) (ret string,
|
||||
}
|
||||
|
||||
func (o *YouTube) GrabTranscript(videoId string, language string) (ret string, err error) {
|
||||
var transcript string
|
||||
if transcript, err = o.GrabTranscriptBase(videoId, language); err != nil {
|
||||
err = fmt.Errorf("transcript not available. (%v)", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the XML transcript
|
||||
doc := soup.HTMLParse(transcript)
|
||||
// Extract the text content from the <text> tags
|
||||
textTags := doc.FindAll("text")
|
||||
var textBuilder strings.Builder
|
||||
for _, textTag := range textTags {
|
||||
textBuilder.WriteString(strings.ReplaceAll(textTag.Text(), "'", "'"))
|
||||
textBuilder.WriteString(" ")
|
||||
ret = textBuilder.String()
|
||||
}
|
||||
return
|
||||
// Use yt-dlp for reliable transcript extraction
|
||||
return o.tryMethodYtDlp(videoId, language)
|
||||
}
|
||||
|
||||
func (o *YouTube) GrabTranscriptWithTimestamps(videoId string, language string) (ret string, err error) {
|
||||
var transcript string
|
||||
if transcript, err = o.GrabTranscriptBase(videoId, language); err != nil {
|
||||
err = fmt.Errorf("transcript not available. (%v)", err)
|
||||
// Use yt-dlp for reliable transcript extraction with timestamps
|
||||
return o.tryMethodYtDlpWithTimestamps(videoId, language)
|
||||
}
|
||||
|
||||
// tryMethodYtDlpInternal is a helper function to reduce duplication between
|
||||
// tryMethodYtDlp and tryMethodYtDlpWithTimestamps.
|
||||
func (o *YouTube) tryMethodYtDlpInternal(videoId string, language string, processVTTFileFunc func(filename string) (string, error)) (ret string, err error) {
|
||||
// Check if yt-dlp is available
|
||||
if _, err = exec.LookPath("yt-dlp"); err != nil {
|
||||
err = fmt.Errorf("yt-dlp not found in PATH. Please install yt-dlp to use YouTube transcript functionality")
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the XML transcript
|
||||
doc := soup.HTMLParse(transcript)
|
||||
// Extract the text content from the <text> tags
|
||||
textTags := doc.FindAll("text")
|
||||
var textBuilder strings.Builder
|
||||
for _, textTag := range textTags {
|
||||
// Extract the start and duration attributes
|
||||
start := textTag.Attrs()["start"]
|
||||
dur := textTag.Attrs()["dur"]
|
||||
end := fmt.Sprintf("%f", parseFloat(start)+parseFloat(dur))
|
||||
// Format the timestamps
|
||||
startFormatted := formatTimestamp(parseFloat(start))
|
||||
endFormatted := formatTimestamp(parseFloat(end))
|
||||
text := strings.ReplaceAll(textTag.Text(), "'", "'")
|
||||
textBuilder.WriteString(fmt.Sprintf("[%s - %s] %s\n", startFormatted, endFormatted, text))
|
||||
// Create a temporary directory for yt-dlp output (cross-platform)
|
||||
tempDir := filepath.Join(os.TempDir(), "fabric-youtube-"+videoId)
|
||||
if err = os.MkdirAll(tempDir, 0755); err != nil {
|
||||
err = fmt.Errorf("failed to create temp directory: %v", err)
|
||||
return
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
// Use yt-dlp to get transcript
|
||||
videoURL := "https://www.youtube.com/watch?v=" + videoId
|
||||
outputPath := filepath.Join(tempDir, "%(title)s.%(ext)s")
|
||||
lang_match := language
|
||||
if len(language) > 2 {
|
||||
lang_match = language[:2]
|
||||
}
|
||||
cmd := exec.Command("yt-dlp",
|
||||
"--write-auto-subs",
|
||||
"--sub-lang", lang_match,
|
||||
"--skip-download",
|
||||
"--sub-format", "vtt",
|
||||
"--quiet",
|
||||
"--no-warnings",
|
||||
"-o", outputPath,
|
||||
videoURL)
|
||||
|
||||
var stderr bytes.Buffer
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err = cmd.Run(); err != nil {
|
||||
err = fmt.Errorf("yt-dlp failed: %v, stderr: %s", err, stderr.String())
|
||||
return
|
||||
}
|
||||
|
||||
// Find VTT files using cross-platform approach
|
||||
vttFiles, err := o.findVTTFiles(tempDir, language)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return processVTTFileFunc(vttFiles[0])
|
||||
}
|
||||
|
||||
func (o *YouTube) tryMethodYtDlp(videoId string, language string) (ret string, err error) {
|
||||
return o.tryMethodYtDlpInternal(videoId, language, o.readAndCleanVTTFile)
|
||||
}
|
||||
|
||||
func (o *YouTube) tryMethodYtDlpWithTimestamps(videoId string, language string) (ret string, err error) {
|
||||
return o.tryMethodYtDlpInternal(videoId, language, o.readAndFormatVTTWithTimestamps)
|
||||
}
|
||||
|
||||
func (o *YouTube) readAndCleanVTTFile(filename string) (ret string, err error) {
|
||||
var content []byte
|
||||
if content, err = os.ReadFile(filename); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Convert VTT to plain text
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var textBuilder strings.Builder
|
||||
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
// Skip WEBVTT header, timestamps, and empty lines
|
||||
if line == "" || line == "WEBVTT" || strings.Contains(line, "-->") ||
|
||||
strings.HasPrefix(line, "NOTE") || strings.HasPrefix(line, "STYLE") ||
|
||||
strings.HasPrefix(line, "Kind:") || strings.HasPrefix(line, "Language:") ||
|
||||
isTimeStamp(line) {
|
||||
continue
|
||||
}
|
||||
// Remove VTT formatting tags
|
||||
line = removeVTTTags(line)
|
||||
if line != "" {
|
||||
textBuilder.WriteString(line)
|
||||
textBuilder.WriteString(" ")
|
||||
}
|
||||
}
|
||||
|
||||
ret = strings.TrimSpace(textBuilder.String())
|
||||
if ret == "" {
|
||||
err = fmt.Errorf("no transcript content found in VTT file")
|
||||
}
|
||||
ret = textBuilder.String()
|
||||
return
|
||||
}
|
||||
|
||||
func parseFloat(s string) float64 {
|
||||
f, _ := strconv.ParseFloat(s, 64)
|
||||
return f
|
||||
}
|
||||
|
||||
func formatTimestamp(seconds float64) string {
|
||||
hours := int(seconds) / 3600
|
||||
minutes := (int(seconds) % 3600) / 60
|
||||
secs := int(seconds) % 60
|
||||
return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, secs)
|
||||
}
|
||||
|
||||
func (o *YouTube) GrabTranscriptBase(videoId string, language string) (ret string, err error) {
|
||||
if err = o.initService(); err != nil {
|
||||
func (o *YouTube) readAndFormatVTTWithTimestamps(filename string) (ret string, err error) {
|
||||
var content []byte
|
||||
if content, err = os.ReadFile(filename); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
watchUrl := "https://www.youtube.com/watch?v=" + videoId
|
||||
var resp string
|
||||
if resp, err = soup.Get(watchUrl); err != nil {
|
||||
return
|
||||
}
|
||||
// Parse VTT and preserve timestamps
|
||||
lines := strings.Split(string(content), "\n")
|
||||
var textBuilder strings.Builder
|
||||
var currentTimestamp string
|
||||
|
||||
doc := soup.HTMLParse(resp)
|
||||
scriptTags := doc.FindAll("script")
|
||||
for _, scriptTag := range scriptTags {
|
||||
if strings.Contains(scriptTag.Text(), "captionTracks") {
|
||||
regex := regexp.MustCompile(`"captionTracks":(\[.*?\])`)
|
||||
match := regex.FindStringSubmatch(scriptTag.Text())
|
||||
if len(match) > 1 {
|
||||
var captionTracks []struct {
|
||||
BaseURL string `json:"baseUrl"`
|
||||
}
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
if err = json.Unmarshal([]byte(match[1]), &captionTracks); err != nil {
|
||||
return
|
||||
}
|
||||
// Skip WEBVTT header and empty lines
|
||||
if line == "" || line == "WEBVTT" || strings.HasPrefix(line, "NOTE") ||
|
||||
strings.HasPrefix(line, "STYLE") || strings.HasPrefix(line, "Kind:") ||
|
||||
strings.HasPrefix(line, "Language:") {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(captionTracks) > 0 {
|
||||
transcriptURL := captionTracks[0].BaseURL
|
||||
for _, captionTrack := range captionTracks {
|
||||
parsedUrl, error := url.Parse(captionTrack.BaseURL)
|
||||
if error != nil {
|
||||
err = fmt.Errorf("error parsing caption track")
|
||||
}
|
||||
parsedUrlParams, _ := url.ParseQuery(parsedUrl.RawQuery)
|
||||
if parsedUrlParams["lang"][0] == language {
|
||||
transcriptURL = captionTrack.BaseURL
|
||||
}
|
||||
}
|
||||
ret, err = soup.Get(transcriptURL)
|
||||
return
|
||||
}
|
||||
// Check if this line is a timestamp
|
||||
if strings.Contains(line, "-->") {
|
||||
// Extract start time for this segment
|
||||
parts := strings.Split(line, " --> ")
|
||||
if len(parts) >= 1 {
|
||||
currentTimestamp = formatVTTTimestamp(parts[0])
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip numeric sequence identifiers
|
||||
if isTimeStamp(line) && !strings.Contains(line, ":") {
|
||||
continue
|
||||
}
|
||||
|
||||
// This should be transcript text
|
||||
if line != "" {
|
||||
// Remove VTT formatting tags
|
||||
cleanText := removeVTTTags(line)
|
||||
if cleanText != "" && currentTimestamp != "" {
|
||||
textBuilder.WriteString(fmt.Sprintf("[%s] %s\n", currentTimestamp, cleanText))
|
||||
}
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("transcript not found")
|
||||
|
||||
ret = strings.TrimSpace(textBuilder.String())
|
||||
if ret == "" {
|
||||
err = fmt.Errorf("no transcript content found in VTT file")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func formatVTTTimestamp(vttTime string) string {
|
||||
// VTT timestamps are in format "00:00:01.234" - convert to "00:00:01"
|
||||
parts := strings.Split(vttTime, ".")
|
||||
if len(parts) > 0 {
|
||||
return parts[0]
|
||||
}
|
||||
return vttTime
|
||||
}
|
||||
|
||||
func isTimeStamp(s string) bool {
|
||||
// Match timestamps like "00:00:01.234" or just numbers
|
||||
timestampRegex := regexp.MustCompile(`^\d+$|^\d{2}:\d{2}:\d{2}`)
|
||||
return timestampRegex.MatchString(s)
|
||||
}
|
||||
|
||||
func removeVTTTags(s string) string {
|
||||
// Remove VTT tags like <c.colorE5E5E5>, </c>, etc.
|
||||
tagRegex := regexp.MustCompile(`<[^>]*>`)
|
||||
return tagRegex.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
func (o *YouTube) GrabComments(videoId string) (ret []string, err error) {
|
||||
if err = o.initService(); err != nil {
|
||||
return
|
||||
@@ -411,6 +490,41 @@ func (o *YouTube) normalizeFileName(name string) string {
|
||||
|
||||
}
|
||||
|
||||
// findVTTFiles searches for VTT files in a directory using cross-platform approach
|
||||
func (o *YouTube) findVTTFiles(dir, language string) ([]string, error) {
|
||||
var vttFiles []string
|
||||
|
||||
// Walk through the directory to find VTT files
|
||||
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() && strings.HasSuffix(strings.ToLower(path), ".vtt") {
|
||||
vttFiles = append(vttFiles, path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to walk directory: %v", err)
|
||||
}
|
||||
|
||||
if len(vttFiles) == 0 {
|
||||
return nil, fmt.Errorf("no VTT files found in directory")
|
||||
}
|
||||
|
||||
// Prefer files with the specified language
|
||||
for _, file := range vttFiles {
|
||||
if strings.Contains(file, "."+language+".vtt") {
|
||||
return []string{file}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return the first VTT file found if no language-specific file exists
|
||||
return []string{vttFiles[0]}, nil
|
||||
}
|
||||
|
||||
type VideoMeta struct {
|
||||
Id string
|
||||
Title string
|
||||
|
||||
@@ -24,12 +24,13 @@ type ChatHandler struct {
|
||||
}
|
||||
|
||||
type PromptRequest struct {
|
||||
UserInput string `json:"userInput"`
|
||||
Vendor string `json:"vendor"`
|
||||
Model string `json:"model"`
|
||||
ContextName string `json:"contextName"`
|
||||
PatternName string `json:"patternName"`
|
||||
StrategyName string `json:"strategyName"` // Optional strategy name
|
||||
UserInput string `json:"userInput"`
|
||||
Vendor string `json:"vendor"`
|
||||
Model string `json:"model"`
|
||||
ContextName string `json:"contextName"`
|
||||
PatternName string `json:"patternName"`
|
||||
StrategyName string `json:"strategyName"` // Optional strategy name
|
||||
Variables map[string]string `json:"variables,omitempty"` // Pattern variables
|
||||
}
|
||||
|
||||
type ChatRequest struct {
|
||||
@@ -118,9 +119,10 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
||||
Role: "user",
|
||||
Content: p.UserInput,
|
||||
},
|
||||
PatternName: p.PatternName,
|
||||
ContextName: p.ContextName,
|
||||
Language: request.Language, // Pass the language field
|
||||
PatternName: p.PatternName,
|
||||
ContextName: p.ContextName,
|
||||
PatternVariables: p.Variables, // Pass pattern variables
|
||||
Language: request.Language, // Pass the language field
|
||||
}
|
||||
|
||||
opts := &common.ChatOptions{
|
||||
|
||||
105
restapi/docs/API_VARIABLES_EXAMPLE.md
Normal file
105
restapi/docs/API_VARIABLES_EXAMPLE.md
Normal file
@@ -0,0 +1,105 @@
|
||||
# REST API Pattern Variables Example
|
||||
|
||||
This example demonstrates how to use pattern variables in REST API calls to the `/chat` endpoint.
|
||||
|
||||
## Example: Using the `translate` pattern with variables
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"prompts": [
|
||||
{
|
||||
"userInput": "Hello my name is Kayvan",
|
||||
"patternName": "translate",
|
||||
"model": "gpt-4o",
|
||||
"vendor": "openai",
|
||||
"contextName": "",
|
||||
"strategyName": "",
|
||||
"variables": {
|
||||
"lang_code": "fr"
|
||||
}
|
||||
}
|
||||
],
|
||||
"language": "en",
|
||||
"temperature": 0.7,
|
||||
"topP": 0.9,
|
||||
"frequencyPenalty": 0.0,
|
||||
"presencePenalty": 0.0
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern Content
|
||||
|
||||
The `translate` pattern contains:
|
||||
|
||||
```markdown
|
||||
You are an expert translator... translate them as accurately and perfectly as possible into the language specified by its language code {{lang_code}}...
|
||||
|
||||
...
|
||||
|
||||
- Translate the document as accurately as possible keeping a 1:1 copy of the original text translated to {{lang_code}}.
|
||||
|
||||
{{input}}
|
||||
```
|
||||
|
||||
### How it works
|
||||
|
||||
1. The pattern is loaded from `patterns/translate/system.md`
|
||||
2. The `{{lang_code}}` variable is replaced with `"fr"` from the variables map
|
||||
3. The `{{input}}` placeholder is replaced with `"Hello my name is Kayvan"`
|
||||
4. The resulting processed pattern is sent to the AI model
|
||||
|
||||
### Expected Result
|
||||
|
||||
The AI would receive a prompt asking it to translate "Hello my name is Kayvan" to French (fr), and would respond with something like "Bonjour, je m'appelle Kayvan".
|
||||
|
||||
## Testing with curl
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/chat \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"prompts": [
|
||||
{
|
||||
"userInput": "Hello my name is Kayvan",
|
||||
"patternName": "translate",
|
||||
"model": "gpt-4o",
|
||||
"vendor": "openai",
|
||||
"variables": {
|
||||
"lang_code": "fr"
|
||||
}
|
||||
}
|
||||
],
|
||||
"temperature": 0.7
|
||||
}'
|
||||
```
|
||||
|
||||
## Multiple Variables Example
|
||||
|
||||
For patterns that use multiple variables:
|
||||
|
||||
```json
|
||||
{
|
||||
"prompts": [
|
||||
{
|
||||
"userInput": "Analyze this business model",
|
||||
"patternName": "custom_analysis",
|
||||
"model": "gpt-4o",
|
||||
"variables": {
|
||||
"role": "expert consultant",
|
||||
"experience": "15",
|
||||
"focus_areas": "revenue, scalability, market fit",
|
||||
"output_format": "bullet points"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- Variables are passed in the `variables` field as a key-value map
|
||||
- Variables are processed using Go's template system
|
||||
- The `{{input}}` variable is automatically handled and should not be included in the variables map
|
||||
- Variables support the same features as CLI variables (plugins, extensions, etc.)
|
||||
@@ -15,20 +15,70 @@ type PatternsHandler struct {
|
||||
|
||||
// NewPatternsHandler creates a new PatternsHandler
|
||||
func NewPatternsHandler(r *gin.Engine, patterns *fsdb.PatternsEntity) (ret *PatternsHandler) {
|
||||
ret = &PatternsHandler{
|
||||
StorageHandler: NewStorageHandler(r, "patterns", patterns), patterns: patterns}
|
||||
// Create a storage handler but don't register any routes yet
|
||||
storageHandler := &StorageHandler[fsdb.Pattern]{storage: patterns}
|
||||
ret = &PatternsHandler{StorageHandler: storageHandler, patterns: patterns}
|
||||
|
||||
// TODO: Add custom, replacement routes here
|
||||
//r.GET("/patterns/:name", ret.Get)
|
||||
// Register routes manually - use custom Get for patterns, others from StorageHandler
|
||||
r.GET("/patterns/:name", ret.Get) // Custom method with variables support
|
||||
r.GET("/patterns/names", ret.GetNames) // From StorageHandler
|
||||
r.DELETE("/patterns/:name", ret.Delete) // From StorageHandler
|
||||
r.GET("/patterns/exists/:name", ret.Exists) // From StorageHandler
|
||||
r.PUT("/patterns/rename/:oldName/:newName", ret.Rename) // From StorageHandler
|
||||
r.POST("/patterns/:name", ret.Save) // From StorageHandler
|
||||
// Add POST route for patterns with variables in request body
|
||||
r.POST("/patterns/:name/apply", ret.ApplyPattern)
|
||||
return
|
||||
}
|
||||
|
||||
// Get handles the GET /patterns/:name route
|
||||
// Get handles the GET /patterns/:name route - returns raw pattern without variable processing
|
||||
func (h *PatternsHandler) Get(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
variables := make(map[string]string) // Assuming variables are passed somehow
|
||||
input := "" // Assuming input is passed somehow
|
||||
pattern, err := h.patterns.GetApplyVariables(name, variables, input)
|
||||
|
||||
// Get the raw pattern content without any variable processing
|
||||
content, err := h.patterns.Load(name + "/" + h.patterns.SystemPatternFile)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Return raw pattern in the same format as the processed patterns
|
||||
pattern := &fsdb.Pattern{
|
||||
Name: name,
|
||||
Description: "",
|
||||
Pattern: string(content),
|
||||
}
|
||||
c.JSON(http.StatusOK, pattern)
|
||||
}
|
||||
|
||||
// PatternApplyRequest represents the request body for applying a pattern
|
||||
type PatternApplyRequest struct {
|
||||
Input string `json:"input"`
|
||||
Variables map[string]string `json:"variables,omitempty"`
|
||||
}
|
||||
|
||||
// ApplyPattern handles the POST /patterns/:name/apply route
|
||||
func (h *PatternsHandler) ApplyPattern(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
|
||||
var request PatternApplyRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Merge query parameters with request body variables (body takes precedence)
|
||||
variables := make(map[string]string)
|
||||
for key, values := range c.Request.URL.Query() {
|
||||
if len(values) > 0 {
|
||||
variables[key] = values[0]
|
||||
}
|
||||
}
|
||||
for key, value := range request.Variables {
|
||||
variables[key] = value
|
||||
}
|
||||
|
||||
pattern, err := h.patterns.GetApplyVariables(name, variables, request.Input)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v1.4.189"
|
||||
var version = "v1.4.213"
|
||||
|
||||
6
web/.browserslistrc
Normal file
6
web/.browserslistrc
Normal file
@@ -0,0 +1,6 @@
|
||||
last 2 versions
|
||||
not dead
|
||||
chrome >= 89
|
||||
firefox >= 89
|
||||
safari >= 15
|
||||
edge >= 89
|
||||
@@ -1,25 +1,83 @@
|
||||
# The Fabric Web App
|
||||
[Installing](#Installing)|[Todos](#Todos)|[Collaborators](#Collaborators)
|
||||
|
||||
This is a web app for Fabric. It was built using [Svelte](https://svelte.dev/), [SkeletonUI](https://skeleton.dev/), and [Mdsvex](https://mdsvex.pngwn.io/).
|
||||
- [The Fabric Web App](#the-fabric-web-app)
|
||||
- [Installing](#installing)
|
||||
- [From Source](#from-source)
|
||||
- [TL;DR: Convenience Scripts](#tldr-convenience-scripts)
|
||||
- [Tips](#tips)
|
||||
- [Obsidian](#obsidian)
|
||||
|
||||
This is a web app for Fabric. It was built using [Svelte][svelte], [SkeletonUI][skeleton], and [Mdsvex][mdsvex].
|
||||
|
||||
The goal of this app is to not only provide a user interface for Fabric, but also an out-of-the-box website for those who want to get started with web development, blogging, or to just have a web interface for fabric. You can use this app as a GUI interface for Fabric, a ready to go blog-site, or a website template for your own projects.
|
||||
|
||||

|
||||

|
||||
|
||||
## Installing
|
||||
|
||||
There are a few days to install and run the Web UI.
|
||||
|
||||
### From Source
|
||||
|
||||
#### TL;DR: Convenience Scripts
|
||||
|
||||
To install the Web UI using `npm`, from the top-level directory:
|
||||
|
||||
```bash
|
||||
./web/scripts/npm-install.sh
|
||||
```
|
||||
|
||||
To use pnpm (preferred and recommended for a huge speed improvement):
|
||||
|
||||
```bash
|
||||
./web/scripts/pnpm-install.sh
|
||||
```
|
||||
|
||||
The app can be run by navigating to the `web` directory and using `npm install`, `pnpm install`, or your preferred package manager. Then simply run `npm run dev`, `pnpm run dev`, or your equivalent command to start the app. *You will need to run fabric in a separate terminal with the `fabric --serve` command.*
|
||||
|
||||
Using npm:
|
||||
|
||||
```bash
|
||||
# Install the GUI and its dependencies
|
||||
npm install
|
||||
# Install PDF-to-Markdown components in this order
|
||||
npm install -D patch-package
|
||||
npm install -D pdfjs-dist
|
||||
npm install -D github:jzillmann/pdf-to-markdown#modularize
|
||||
|
||||
npx svelte-kit sync
|
||||
|
||||
# Now, with "fabric --serve" running already, you can run the GUI
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Using pnpm:
|
||||
|
||||
```bash
|
||||
# Install the GUI and its dependencies
|
||||
pnpm install
|
||||
# Install PDF-to-Markdown components in this order
|
||||
pnpm install -D patch-package
|
||||
pnpm install -D pdfjs-dist
|
||||
pnpm install -D github:jzillmann/pdf-to-markdown#modularize
|
||||
|
||||
pnpm exec svelte-kit sync
|
||||
|
||||
# Now, with "fabric --serve" running already, you can run the GUI
|
||||
pnpm run dev
|
||||
```
|
||||
|
||||
## Tips
|
||||
|
||||
When creating new posts make sure to include a date, description, tags, and aliases. Only a date is needed to display a note.
|
||||
|
||||
You can include images, tags to other articles, code blocks, and more all within your markdown files.
|
||||
|
||||
### If you choose to use Obsidian alongside this app
|
||||
|
||||
You can design and order your vault however you like, though a `posts` folder should be kept in your vault to house any articles you'd like to post.
|
||||
You can include images, tags to other articles, code blocks, and more all within your markdown files.
|
||||
|
||||
## Obsidian
|
||||
|
||||
If you choose to use Obsidian alongside this app,
|
||||
you can design and order your vault however you like, though a `posts` folder should be kept in your vault to house any articles you'd like to post.
|
||||
|
||||
[svelte]: https://svelte.dev/
|
||||
[skeleton]: https://skeleton.dev/
|
||||
[mdsvex]: https://mdsvex.pngwn.io/
|
||||
|
||||
@@ -13,54 +13,54 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@skeletonlabs/skeleton": "^2.8.0",
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@skeletonlabs/skeleton": "^2.11.0",
|
||||
"@skeletonlabs/tw-plugin": "^0.3.1",
|
||||
"@sveltejs/adapter-auto": "^3.0.0",
|
||||
"@sveltejs/kit": "^2.9.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/node": "^20.10.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"@sveltejs/adapter-auto": "^3.3.1",
|
||||
"@sveltejs/kit": "^2.21.1",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.2",
|
||||
"@tailwindcss/forms": "^0.5.10",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"@types/node": "^20.17.50",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"eslint-plugin-svelte": "^2.46.1",
|
||||
"lucide-svelte": "^0.309.0",
|
||||
"mdsvex": "^0.11.2",
|
||||
"patch-package": "^8.0.0",
|
||||
"pdf-to-markdown-core": "github:jzillmann/pdf-to-markdown#modularize",
|
||||
"pdfjs-dist": "^2.5.207",
|
||||
"postcss": "^8.4.49",
|
||||
"pdfjs-dist": "^4.2.67",
|
||||
"postcss": "^8.5.3",
|
||||
"postcss-load-config": "^6.0.1",
|
||||
"rehype-autolink-headings": "^7.1.0",
|
||||
"rehype-slug": "^6.0.0",
|
||||
"shiki": "^1.24.3",
|
||||
"svelte": "^4.2.7",
|
||||
"svelte-check": "^3.6.0",
|
||||
"shiki": "^1.29.2",
|
||||
"svelte": "^4.2.20",
|
||||
"svelte-check": "^3.8.6",
|
||||
"svelte-inview": "^4.0.4",
|
||||
"svelte-markdown": "^0.4.1",
|
||||
"svelte-reveal": "^1.1.0",
|
||||
"svelte-youtube-embed": "^0.3.3",
|
||||
"svelte-youtube-lite": "^0.6.2",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.0.3",
|
||||
"vite-plugin-tailwind-purgecss": "^0.2.0"
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "^5.8.3",
|
||||
"vite": "^5.4.19",
|
||||
"vite-plugin-tailwind-purgecss": "^0.2.1"
|
||||
},
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.5.3",
|
||||
"@floating-ui/dom": "^1.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cn": "^0.1.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"highlight.js": "^11.10.0",
|
||||
"marked": "^15.0.1",
|
||||
"nanoid": "4.0.2",
|
||||
"highlight.js": "^11.11.1",
|
||||
"marked": "^15.0.12",
|
||||
"nanoid": "5.0.9",
|
||||
"rehype": "^13.0.2",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-unwrap-images": "^1.0.0",
|
||||
"tailwind-merge": "^2.5.4",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"vfile-message": "^4.0.2",
|
||||
"yaml": "^2.6.1",
|
||||
"yaml": "^2.8.0",
|
||||
"youtube-transcript": "^1.2.1"
|
||||
},
|
||||
"pnpm": {
|
||||
|
||||
1967
web/pnpm-lock.yaml
generated
1967
web/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
19
web/scripts/npm-install.sh
Executable file
19
web/scripts/npm-install.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")/.." || exit
|
||||
|
||||
if command -v npm &>/dev/null; then
|
||||
echo "npm is installed"
|
||||
else
|
||||
echo "npm is not installed. Please install npm first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install the GUI and its dependencies
|
||||
npm install
|
||||
# Install PDF-to-Markdown components in this order
|
||||
npm install -D patch-package
|
||||
npm install -D pdfjs-dist
|
||||
npm install -D github:jzillmann/pdf-to-markdown#modularize
|
||||
|
||||
npx svelte-kit sync
|
||||
19
web/scripts/pnpm-install.sh
Executable file
19
web/scripts/pnpm-install.sh
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/bin/bash
|
||||
|
||||
cd "$(dirname "$0")/.." || exit
|
||||
|
||||
if command -v npm &>/dev/null; then
|
||||
echo "pnpm is installed"
|
||||
else
|
||||
echo "pnpm is not installed. Please install pnpm first."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Install the GUI and its dependencies
|
||||
pnpm install
|
||||
# Install PDF-to-Markdown components in this order
|
||||
pnpm install -D patch-package
|
||||
pnpm install -D pdfjs-dist
|
||||
pnpm install -D github:jzillmann/pdf-to-markdown#modularize
|
||||
|
||||
pnpm exec svelte-kit sync
|
||||
@@ -3,8 +3,11 @@
|
||||
import Models from "./Models.svelte";
|
||||
import ModelConfig from "./ModelConfig.svelte";
|
||||
import { Select } from "$lib/components/ui/select";
|
||||
import { Input } from "$lib/components/ui/input";
|
||||
import { Label } from "$lib/components/ui/label";
|
||||
import { languageStore } from '$lib/store/language-store';
|
||||
import { strategies, selectedStrategy, fetchStrategies } from '$lib/store/strategy-store';
|
||||
import { patternVariables } from '$lib/store/pattern-store';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
const languages = [
|
||||
@@ -18,6 +21,25 @@
|
||||
{ code: 'it', name: 'Italian' }
|
||||
];
|
||||
|
||||
let variablesJsonString = '';
|
||||
|
||||
// Parse JSON string and update variables store
|
||||
function updateVariables() {
|
||||
try {
|
||||
if (variablesJsonString.trim() === '') {
|
||||
patternVariables.set({});
|
||||
} else {
|
||||
const parsed = JSON.parse(variablesJsonString);
|
||||
if (typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed)) {
|
||||
patternVariables.set(parsed);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Don't update the store if JSON is invalid - just ignore the error
|
||||
// This allows partial typing without breaking
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
fetchStrategies();
|
||||
});
|
||||
@@ -33,7 +55,7 @@
|
||||
<Models />
|
||||
</div>
|
||||
<div>
|
||||
<Select
|
||||
<Select
|
||||
bind:value={$languageStore}
|
||||
class="bg-primary-800/30 border-none hover:bg-primary-800/40 transition-colors"
|
||||
>
|
||||
@@ -43,7 +65,7 @@
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Select
|
||||
<Select
|
||||
bind:value={$selectedStrategy}
|
||||
class="bg-primary-800/30 border-none hover:bg-primary-800/40 transition-colors"
|
||||
>
|
||||
@@ -53,8 +75,19 @@
|
||||
{/each}
|
||||
</Select>
|
||||
</div>
|
||||
<div>
|
||||
<Label for="pattern-variables" class="text-xs text-white/70 mb-1 block">Pattern Variables (JSON)</Label>
|
||||
<textarea
|
||||
id="pattern-variables"
|
||||
bind:value={variablesJsonString}
|
||||
on:input={updateVariables}
|
||||
placeholder="{`{\"lang_code\": \"fr\", \"role\": \"expert\"}`}"
|
||||
class="w-full h-20 px-3 py-2 text-sm bg-primary-800/30 border-none rounded-md hover:bg-primary-800/40 transition-colors text-white placeholder-white/50 resize-none focus:ring-1 focus:ring-white/20 focus:outline-none"
|
||||
style="font-family: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace;"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Right side - Model Config -->
|
||||
<div class="w-[65%]">
|
||||
<ModelConfig />
|
||||
|
||||
62
web/src/lib/config/environment.ts
Normal file
62
web/src/lib/config/environment.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Environment configuration for the Fabric web app
|
||||
* Centralizes all environment variable handling
|
||||
*/
|
||||
|
||||
// Default values
|
||||
const DEFAULT_FABRIC_BASE_URL = 'http://localhost:8080';
|
||||
|
||||
/**
|
||||
* Get the Fabric base URL from environment variable or default
|
||||
* This function works in both server and client contexts
|
||||
*/
|
||||
export function getFabricBaseUrl(): string {
|
||||
// In server context (Node.js), use process.env
|
||||
if (typeof process !== 'undefined' && process.env) {
|
||||
return process.env.FABRIC_BASE_URL || DEFAULT_FABRIC_BASE_URL;
|
||||
}
|
||||
|
||||
// In client context, check if the environment was injected via Vite
|
||||
if (typeof window !== 'undefined' && (window as any).__FABRIC_CONFIG__) {
|
||||
return (window as any).__FABRIC_CONFIG__.FABRIC_BASE_URL || DEFAULT_FABRIC_BASE_URL;
|
||||
}
|
||||
|
||||
// Fallback to default
|
||||
return DEFAULT_FABRIC_BASE_URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Fabric API base URL (adds /api if not present)
|
||||
*/
|
||||
export function getFabricApiUrl(): string {
|
||||
const baseUrl = getFabricBaseUrl();
|
||||
|
||||
// Remove trailing slash if present
|
||||
const cleanBaseUrl = baseUrl.replace(/\/$/, '');
|
||||
|
||||
// Check if it already ends with /api
|
||||
if (cleanBaseUrl.endsWith('/api')) {
|
||||
return cleanBaseUrl;
|
||||
}
|
||||
|
||||
return `${cleanBaseUrl}/api`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration object for easy access to all environment settings
|
||||
*/
|
||||
export const config = {
|
||||
fabricBaseUrl: getFabricBaseUrl(),
|
||||
fabricApiUrl: getFabricApiUrl(),
|
||||
} as const;
|
||||
|
||||
// Type definitions
|
||||
export interface FabricConfig {
|
||||
FABRIC_BASE_URL: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
__FABRIC_CONFIG__?: FabricConfig;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ export interface ChatPrompt {
|
||||
model: string;
|
||||
patternName?: string;
|
||||
strategyName?: string; // Optional strategy name to prepend strategy prompt
|
||||
variables?: { [key: string]: string }; // Pattern variables
|
||||
}
|
||||
|
||||
export interface ChatConfig {
|
||||
|
||||
@@ -6,7 +6,7 @@ import type {
|
||||
} from '$lib/interfaces/chat-interface';
|
||||
import { get } from 'svelte/store';
|
||||
import { modelConfig } from '$lib/store/model-store';
|
||||
import { systemPrompt, selectedPatternName } from '$lib/store/pattern-store';
|
||||
import { systemPrompt, selectedPatternName, patternVariables } from '$lib/store/pattern-store';
|
||||
import { chatConfig } from '$lib/store/chat-config';
|
||||
import { messageStore } from '$lib/store/chat-store';
|
||||
import { languageStore } from '$lib/store/language-store';
|
||||
@@ -75,48 +75,46 @@ export class ChatService {
|
||||
|
||||
private cleanPatternOutput(content: string): string {
|
||||
// Remove markdown fence if present
|
||||
content = content.replace(/^```markdown\n/, '');
|
||||
content = content.replace(/\n```$/, '');
|
||||
|
||||
let cleaned = content.replace(/^```markdown\n/, '');
|
||||
cleaned = cleaned.replace(/\n```$/, '');
|
||||
|
||||
// Existing cleaning
|
||||
content = content.replace(/^# OUTPUT\s*\n/, '');
|
||||
content = content.replace(/^\s*\n/, '');
|
||||
content = content.replace(/\n\s*$/, '');
|
||||
content = content.replace(/^#\s+([A-Z]+):/gm, '$1:');
|
||||
content = content.replace(/^#\s+([A-Z]+)\s*$/gm, '$1');
|
||||
content = content.trim();
|
||||
content = content.replace(/\n{3,}/g, '\n\n');
|
||||
return content;
|
||||
cleaned = cleaned.replace(/^# OUTPUT\s*\n/, '');
|
||||
cleaned = cleaned.replace(/^\s*\n/, '');
|
||||
cleaned = cleaned.replace(/\n\s*$/, '');
|
||||
cleaned = cleaned.replace(/^#\s+([A-Z]+):/gm, '$1:');
|
||||
cleaned = cleaned.replace(/^#\s+([A-Z]+)\s*$/gm, '$1');
|
||||
cleaned = cleaned.trim();
|
||||
cleaned = cleaned.replace(/\n{3,}/g, '\n\n');
|
||||
return cleaned;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private createMessageStream(reader: ReadableStreamDefaultReader<Uint8Array>): ReadableStream<StreamResponse> {
|
||||
let buffer = '';
|
||||
const cleanPatternOutput = this.cleanPatternOutput.bind(this);
|
||||
const language = get(languageStore);
|
||||
const validator = new LanguageValidator(language);
|
||||
|
||||
const processResponse = (response: StreamResponse) => {
|
||||
const pattern = get(selectedPatternName);
|
||||
|
||||
if (pattern) {
|
||||
response.content = cleanPatternOutput(response.content);
|
||||
// Simplified format determination - always markdown unless mermaid
|
||||
const isMermaid = [
|
||||
'graph TD', 'gantt', 'flowchart',
|
||||
'sequenceDiagram', 'classDiagram', 'stateDiagram'
|
||||
].some(starter => response.content.trim().startsWith(starter));
|
||||
|
||||
response.format = isMermaid ? 'mermaid' : 'markdown';
|
||||
}
|
||||
|
||||
if (response.type === 'content') {
|
||||
response.content = validator.enforceLanguage(response.content);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
private createMessageStream(reader: ReadableStreamDefaultReader<Uint8Array>): ReadableStream<StreamResponse> {
|
||||
let buffer = '';
|
||||
const cleanPatternOutput = this.cleanPatternOutput.bind(this);
|
||||
const language = get(languageStore);
|
||||
const validator = new LanguageValidator(language);
|
||||
|
||||
const processResponse = (response: StreamResponse) => {
|
||||
const pattern = get(selectedPatternName);
|
||||
|
||||
if (pattern) {
|
||||
response.content = cleanPatternOutput(response.content);
|
||||
// Simplified format determination - always markdown unless mermaid
|
||||
const isMermaid = [
|
||||
'graph TD', 'gantt', 'flowchart',
|
||||
'sequenceDiagram', 'classDiagram', 'stateDiagram'
|
||||
].some(starter => response.content.trim().startsWith(starter));
|
||||
|
||||
response.format = isMermaid ? 'mermaid' : 'markdown';
|
||||
}
|
||||
|
||||
if (response.type === 'content') {
|
||||
response.content = validator.enforceLanguage(response.content);
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
return new ReadableStream({
|
||||
async start(controller) {
|
||||
try {
|
||||
@@ -162,18 +160,18 @@ export class ChatService {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private createChatPrompt(userInput: string, systemPromptText?: string): ChatPrompt {
|
||||
const config = get(modelConfig);
|
||||
const language = get(languageStore);
|
||||
|
||||
const languageInstruction = language !== 'en'
|
||||
|
||||
const languageInstruction = language !== 'en'
|
||||
? `You MUST respond in ${language} language. All output must be in ${language}. `
|
||||
// ? `You MUST respond in ${language} language. ALL output, including section headers, titles, and formatting, MUST be translated into ${language}. It is CRITICAL that you translate ALL headers, such as SUMMARY, IDEAS, QUOTES, TAKEAWAYS, MAIN POINTS, etc., into ${language}. Maintain markdown formatting in the response. Do not output any English headers.`
|
||||
: '';
|
||||
|
||||
|
||||
const finalSystemPrompt = languageInstruction + (systemPromptText ?? get(systemPrompt));
|
||||
|
||||
|
||||
const finalUserInput = language !== 'en'
|
||||
? `${userInput}\n\nIMPORTANT: Respond in ${language} language only.`
|
||||
: userInput;
|
||||
@@ -183,15 +181,11 @@ export class ChatService {
|
||||
systemPrompt: finalSystemPrompt,
|
||||
model: config.model,
|
||||
patternName: get(selectedPatternName),
|
||||
strategyName: get(selectedStrategy) // Add selected strategy to prompt
|
||||
strategyName: get(selectedStrategy), // Add selected strategy to prompt
|
||||
variables: get(patternVariables) // Add pattern variables
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public async createChatRequest(userInput: string, systemPromptText?: string, isPattern: boolean = false): Promise<ChatRequest> {
|
||||
const prompt = this.createChatPrompt(userInput, systemPromptText);
|
||||
const config = get(chatConfig);
|
||||
@@ -221,16 +215,16 @@ export class ChatService {
|
||||
onError: (error: Error) => void
|
||||
): Promise<void> {
|
||||
const reader = stream.getReader();
|
||||
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
|
||||
|
||||
if (value.type === 'error') {
|
||||
throw new ChatError(value.content, 'STREAM_CONTENT_ERROR');
|
||||
}
|
||||
|
||||
|
||||
if (value.type === 'content') {
|
||||
onContent(value.content, value);
|
||||
}
|
||||
@@ -239,11 +233,7 @@ export class ChatService {
|
||||
onError(error instanceof ChatError ? error : new ChatError('Stream processing error', 'STREAM_ERROR', error));
|
||||
} finally {
|
||||
reader.releaseLock();
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { createPipeline, transformers } from 'pdf-to-markdown-core/lib/src';
|
||||
import { createPipeline, transformers } from 'pdf-to-markdown-core/lib/src';
|
||||
import { PARSE_SCHEMA } from 'pdf-to-markdown-core/lib/src/PdfParser';
|
||||
import * as pdfjs from 'pdfjs-dist';
|
||||
import pdfConfig from './pdf-config';
|
||||
|
||||
export class PdfConversionService {
|
||||
constructor() {
|
||||
if (typeof window !== 'undefined') {
|
||||
console.log('PDF.js version:', pdfjs.version);
|
||||
const workerUrl = new URL(
|
||||
'pdfjs-dist/build/pdf.worker.min.js',
|
||||
import.meta.url
|
||||
);
|
||||
console.log('Worker URL:', workerUrl.href);
|
||||
pdfjs.GlobalWorkerOptions.workerSrc = workerUrl.href;
|
||||
// Initialize PDF.js configuration from the shared config
|
||||
pdfConfig.initialize();
|
||||
console.log('Worker configuration complete');
|
||||
}
|
||||
}
|
||||
|
||||
20
web/src/lib/services/pdf-config.ts
Normal file
20
web/src/lib/services/pdf-config.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { browser } from '$app/environment';
|
||||
import { GlobalWorkerOptions } from 'pdfjs-dist';
|
||||
|
||||
// Set up the worker source location - point to static file in public directory
|
||||
const workerSrc = '/pdf.worker.min.mjs';
|
||||
|
||||
// Configure the worker options only on the client side
|
||||
if (browser) {
|
||||
GlobalWorkerOptions.workerSrc = workerSrc;
|
||||
}
|
||||
|
||||
// Export the configuration
|
||||
export default {
|
||||
initialize: () => {
|
||||
if (browser) {
|
||||
console.log('PDF.js worker initialized at', workerSrc);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,11 +15,11 @@ export const patterns = derived(
|
||||
return $allPatterns.filter(p => {
|
||||
// Keep all patterns if no language is selected
|
||||
if (!$language) return true;
|
||||
|
||||
|
||||
// Check if pattern has a language prefix (e.g., en_, fr_)
|
||||
const match = p.Name.match(/^([a-z]{2})_/);
|
||||
if (!match) return true; // Keep patterns without language prefix
|
||||
|
||||
|
||||
// Only filter out patterns that have a different language prefix
|
||||
const patternLang = match[1];
|
||||
return patternLang === $language;
|
||||
@@ -30,6 +30,9 @@ export const patterns = derived(
|
||||
export const systemPrompt = writable<string>('');
|
||||
export const selectedPatternName = writable<string>('');
|
||||
|
||||
// Pattern variables store
|
||||
export const patternVariables = writable<Record<string, string>>({});
|
||||
|
||||
export const setSystemPrompt = (prompt: string) => {
|
||||
console.log('Setting system prompt:', prompt);
|
||||
systemPrompt.set(prompt);
|
||||
@@ -60,13 +63,13 @@ export const patternAPI = {
|
||||
const patternResponse = await fetch(`/api/patterns/${pattern}`);
|
||||
const patternData = await patternResponse.json();
|
||||
console.log(`Pattern ${pattern} content length:`, patternData.Pattern?.length || 0);
|
||||
|
||||
|
||||
// Find matching description from JSON
|
||||
const desc = descriptions.find(d => d.patternName === pattern);
|
||||
if (!desc) {
|
||||
console.warn(`No description found for pattern: ${pattern}`);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
Name: pattern,
|
||||
Description: desc?.description || pattern.charAt(0).toUpperCase() + pattern.slice(1),
|
||||
|
||||
@@ -15,11 +15,11 @@ export const POST: RequestHandler = async ({ request }) => {
|
||||
language: body.language,
|
||||
hasLanguageParam: true
|
||||
});
|
||||
|
||||
|
||||
// Extract video ID
|
||||
const match = body.url.match(/(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/);
|
||||
const videoId = match ? match[1] : null;
|
||||
|
||||
|
||||
if (!videoId) {
|
||||
return json({ error: 'Invalid YouTube URL' }, { status: 400 });
|
||||
}
|
||||
|
||||
21
web/static/pdf.worker.min.mjs
Normal file
21
web/static/pdf.worker.min.mjs
Normal file
File diff suppressed because one or more lines are too long
@@ -2,13 +2,20 @@ import { purgeCss } from 'vite-plugin-tailwind-purgecss';
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// Get the Fabric base URL from environment variable with fallback
|
||||
const FABRIC_BASE_URL = process.env.FABRIC_BASE_URL || 'http://localhost:8080';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit(), purgeCss()],
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true
|
||||
}
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['pdfjs-dist'],
|
||||
esbuildOptions: {
|
||||
target: 'esnext',
|
||||
supported: {
|
||||
'top-level-await': true
|
||||
}
|
||||
}
|
||||
},
|
||||
define: {
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||
@@ -18,6 +25,10 @@ export default defineConfig({
|
||||
'process.browser': true,
|
||||
'process': {
|
||||
cwd: () => ('/')
|
||||
},
|
||||
// Inject Fabric configuration for client-side access
|
||||
'__FABRIC_CONFIG__': {
|
||||
FABRIC_BASE_URL: JSON.stringify(FABRIC_BASE_URL)
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
@@ -31,11 +42,11 @@ export default defineConfig({
|
||||
},
|
||||
proxy: {
|
||||
'/api': {
|
||||
target: 'http://localhost:8080',
|
||||
target: FABRIC_BASE_URL,
|
||||
changeOrigin: true,
|
||||
timeout: 30000,
|
||||
rewrite: (path) => path.replace(/^\/api/, ''),
|
||||
configure: (proxy, options) => {
|
||||
configure: (proxy, _options) => {
|
||||
proxy.on('error', (err, req, res) => {
|
||||
console.log('proxy error', err);
|
||||
res.writeHead(500, {
|
||||
@@ -46,10 +57,10 @@ export default defineConfig({
|
||||
}
|
||||
},
|
||||
'^/(patterns|models|sessions)/names': {
|
||||
target: 'http://localhost:8080',
|
||||
target: FABRIC_BASE_URL,
|
||||
changeOrigin: true,
|
||||
timeout: 30000,
|
||||
configure: (proxy, options) => {
|
||||
configure: (proxy, _options) => {
|
||||
proxy.on('error', (err, req, res) => {
|
||||
console.log('proxy error', err);
|
||||
res.writeHead(500, {
|
||||
@@ -66,4 +77,16 @@ export default defineConfig({
|
||||
ignored: ['**/node_modules/**', '**/dist/**', '**/.git/**', '**/.svelte-kit/**']
|
||||
}
|
||||
},
|
||||
build: {
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true
|
||||
},
|
||||
target: 'esnext',
|
||||
minify: true,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
format: 'es'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user