Compare commits

...

60 Commits

Author SHA1 Message Date
github-actions[bot]
730d0adc86 Update version to v1.4.211 and commit 2025-06-19 21:47:20 +00:00
Kayvan Sylvan
dc9168ab6f Merge pull request #1533 from ksylvan/0619-enhance-restapi-and-webui-with-variables
REST API and Web UI Now Support Dynamic Pattern Variables
2025-06-19 14:45:48 -07:00
Daniel Miessler
e500a5916e Updated paper analyzer. Went back to my own format. 2025-06-19 14:45:31 -07:00
Kayvan Sylvan
6ddf46a379 chore: removed a directory of raycast scripts sitting in the patterns/ directory 2025-06-19 14:11:29 -07:00
Kayvan Sylvan
e8aa358b15 refactor(ChatService): clean up message stream and pattern output methods
- Refactor `cleanPatternOutput` to use a dedicated return variable.
- Hoist `processResponse` function for improved stream readability.
- Remove unnecessary whitespace and trailing newlines from file.
2025-06-19 13:55:25 -07:00
Daniel Miessler
62f373c2b4 Updated paper analyzer. 2025-06-19 13:55:03 -07:00
Daniel Miessler
fcf826f3de Updated paper analyzer. 2025-06-19 13:48:57 -07:00
Kayvan Sylvan
bd2db29cee feat: add ApplyPattern route for applying patterns with variables
## CHANGES
- Create `PatternApplyRequest` struct for request body parsing
- Implement `ApplyPattern` method for POST /patterns/:name/apply
- Register manual routes for pattern operations in `NewPatternsHandler`
- Refactor `Get` method to return raw pattern content
- Merge query parameters with request body variables in `ApplyPattern`
- Use `StorageHandler` for pattern-related storage operations
2025-06-19 13:30:56 -07:00
Kayvan Sylvan
c6d612ee9a feat: add pattern variables support to REST API chat endpoint
## CHANGES

- Add Variables field to PromptRequest struct
- Pass pattern variables through chat handler
- Create API variables documentation example
- Add pattern variables UI in web interface
- Create pattern variables store in Svelte
- Include variables in chat service requests
- Add JSON textarea for variable input
2025-06-19 13:10:05 -07:00
Daniel Miessler
d613c25974 Updated sanitization instructions. 2025-06-19 12:24:09 -07:00
Daniel Miessler
c0abea7c66 Updated markdown cleaner. 2025-06-19 12:02:21 -07:00
Daniel Miessler
496bd2812a Updated markdown cleaner. 2025-06-19 11:34:09 -07:00
github-actions[bot]
70fccaf2fb Update version to v1.4.210 and commit 2025-06-18 07:40:11 +00:00
Kayvan Sylvan
9a71f7c96d Merge pull request #1530 from ksylvan/0617-add-citations-to-perplexity
Add Citation Support to Perplexity Response
2025-06-18 00:38:37 -07:00
Kayvan Sylvan
5da3db383d feat: add citation support to perplexity AI responses
## CHANGES

- Add citation extraction from API responses
- Append citations section to response content
- Format citations as numbered markdown list
- Handle citations in streaming responses
- Store last response for citation access
- Add citations after stream completion
- Maintain backward compatibility with responses
2025-06-17 20:45:03 -07:00
Daniel Miessler 🛡️
19438cbd20 Update README.md 2025-06-17 11:52:02 -07:00
Daniel Miessler 🛡️
a0b71ee365 Update README.md
Updated readme.
2025-06-17 11:48:44 -07:00
Daniel Miessler 🛡️
034513ece5 Update README.md
An update to the intro text, describing Fabric's utility to most people.
2025-06-17 11:45:46 -07:00
github-actions[bot]
0affb9bab1 Update version to v1.4.209 and commit 2025-06-17 10:21:02 +00:00
github-actions[bot]
3305df8fb2 Update version to v1.4.208 and commit 2025-06-17 10:19:28 +00:00
Kayvan Sylvan
892c229076 Merge pull request #1527 from ksylvan/0617-add-perplexity-vendor
Add Perplexity AI Provider with Token Limits Support
2025-06-17 03:17:57 -07:00
Kayvan Sylvan
599c5f2b9f Merge pull request #1526 from ConnorKirk/check-for-aws-credentials
Check for AWS_PROFILE or AWS_ROLE_SESSION_NAME environment variables
2025-06-17 03:17:48 -07:00
Kayvan Sylvan
19e5d8dbe0 chore: update README with Perplexity AI support instructions
### CHANGES
- Add instructions for configuring Perplexity AI with Fabric
- Include example command for querying Perplexity AI
- Retain existing instructions for YouTube transcription changes
2025-06-17 02:57:37 -07:00
Kayvan Sylvan
b772127738 feat: add Perplexity AI provider support with token limits and streaming
## CHANGES

- feat: Add `MaxTokens` field to `ChatOptions` struct for response control
- feat: Integrate Perplexity client into core plugin registry initialization
- build: Add perplexity-go/v2 dependency to enable API interactions
- feat: Implement stream handling in Perpexlty client using sync.WaitGroup
- fix: Correct parameter types for penalty options in API requests
## LINKS

<https://github.com/sgaunet/perlexipty-go> - Client library used
2025-06-17 02:32:53 -07:00
Connor Kirkpatrick
5dd61abe2a Check for AWS_PROFILE or AWS_ROLE_SESSION_NAME environment variables 2025-06-17 10:25:17 +01:00
github-actions[bot]
f45e140126 Update version to v1.4.207 and commit 2025-06-17 07:41:51 +00:00
Kayvan Sylvan
752a66cb48 Merge pull request #1525 from ksylvan/0617-fix-lang-code-vtt-youtube-transcript-bug
Refactor yt-dlp Transcript Logic and Fix Language Bug
2025-06-17 00:40:18 -07:00
Kayvan Sylvan
da28d91d65 refactor: extract common yt-dlp logic to reduce code duplication in YouTube plugin
## CHANGES

- Extract shared yt-dlp logic into tryMethodYtDlpInternal helper
- Add processVTTFileFunc parameter for flexible VTT processing
- Implement language matching for 2-char language codes
- Refactor tryMethodYtDlp to use new helper function
- Refactor tryMethodYtDlpWithTimestamps to use helper
- Reduce code duplication between transcript methods
- Maintain existing functionality with cleaner structure
2025-06-17 00:32:33 -07:00
Daniel Miessler
5a66ca1c5a Updated extract insights. 2025-06-16 16:43:21 -07:00
Daniel Miessler
98f3da610b Updated extract insights. 2025-06-16 16:41:14 -07:00
github-actions[bot]
73ce92ccd9 Update version to v1.4.206 and commit 2025-06-16 23:12:53 +00:00
Kayvan Sylvan
7f3f1d641f Merge pull request #1523 from ksylvan/0616-bedrock-plugin-config-fix
Conditional AWS Bedrock Plugin Initialization
2025-06-16 16:10:59 -07:00
Kayvan Sylvan
44b5c46beb feat: add AWS credential detection for Bedrock client initialization
## CHANGES

- Add hasAWSCredentials helper function
- Check for AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
- Look for AWS shared credentials file
- Support custom AWS_SHARED_CREDENTIALS_FILE path
- Default to ~/.aws/credentials location
- Only initialize Bedrock client if credentials exist
- Prevent AWS SDK credential search failures
2025-06-16 15:11:58 -07:00
Daniel Miessler
8d37c9d6b9 Updated prompt. 2025-06-16 13:26:13 -07:00
github-actions[bot]
1138d0b60e Update version to v1.4.205 and commit 2025-06-16 13:26:26 +00:00
Kayvan Sylvan
b78217088d Merge pull request #1519 from ConnorKirk/bedrock-plugin-dynamically-fetch-models 2025-06-16 06:24:54 -07:00
Connor Kirkpatrick
76b889733d Dynamically fetch and list available foundation models and inference profiles 2025-06-16 11:05:34 +01:00
Kayvan Sylvan
3911fd9f5d Merge pull request #1518 from ksylvan/0615-remove-old-redundant-patterns
chore: remove duplicate/outdated patterns
2025-06-15 12:56:31 -07:00
Daniel Miessler
b06e29f8a8 Updated markdown sanitizer. 2025-06-15 12:52:39 -07:00
Kayvan Sylvan
11a7e542e1 chore: remove duplicate/outdated patterns 2025-06-15 12:47:08 -07:00
Daniel Miessler
6681078259 Updated markdown cleaner. 2025-06-15 12:45:34 -07:00
Daniel Miessler
be1edf7b1d Updated markdown cleaner. 2025-06-15 12:44:15 -07:00
github-actions[bot]
8ce748a1b1 Update version to v1.4.204 and commit 2025-06-15 05:53:11 +00:00
Kayvan Sylvan
96070f6f39 Merge pull request #1517 from ksylvan/0614-prevent-race-conditions-tag-and-release
Fix: Prevent race conditions in versioning workflow.
2025-06-14 22:51:39 -07:00
Kayvan Sylvan
ca3e89a889 ci: improve version update workflow to prevent race conditions
### CHANGES

- Add concurrency control to prevent simultaneous runs
- Pull latest main branch changes before tagging
- Fetch all remote tags before calculating version
2025-06-14 22:30:54 -07:00
github-actions[bot]
47d799d7ae Update version to v1.4.203 and commit 2025-06-14 06:01:13 +00:00
Eugen Eisler
4899ce56a5 Merge pull request #1512 from ConnorKirk/1500-add-support-for-amazon-bedrock
feat:Add support for Amazon Bedrock
2025-06-14 07:59:41 +02:00
Eugen Eisler
4a7b7becec Merge pull request #1513 from marcas756/feature/create_mnemonic_phrases
feat: create mnemonic phrase pattern
2025-06-14 07:53:05 +02:00
Eugen Eisler
80fdccbe89 Merge pull request #1516 from ksylvan/0612-fix-REST-api-put-pattern
Fix REST API pattern creation
2025-06-14 07:52:06 +02:00
Kayvan Sylvan
d9d8f7bf96 feat: add Save method to PatternsEntity for persisting patterns to filesystem
## CHANGES

- Add Save method to PatternsEntity struct
- Create pattern directory with proper permissions
- Write pattern content to system pattern file
- Add comprehensive test for Save functionality
- Verify directory creation and file contents
- Handle errors for directory and file operations
2025-06-13 15:52:01 -07:00
Marco Bacchi
a96ddbeef0 feat: create mnemonic phrase pattern
Add a new pattern for generating mnemonic phrases from diceware words. This includes two markdown files defining the user guide, and system implementation details.
2025-06-12 23:27:08 +02:00
Connor Kirkpatrick
d32a1d6a5a Add Bedrock plugin
This commits adds support for using Amazon Bedrock within fabric.
2025-06-12 13:07:12 +01:00
github-actions[bot]
201474791d Update version to v1.4.202 and commit 2025-06-12 05:47:10 +00:00
Eugen Eisler
6d09137fee Merge pull request #1510 from ksylvan/0611-fix-youtube-transcript-for-windows
Cross-Platform fix for Youtube Transcript extraction
2025-06-12 07:45:38 +02:00
Kayvan Sylvan
680febbe66 *fix: replace Unix-specific file operations with cross-platform alternatives
## CHANGES

- Replace hardcoded `/tmp` with `os.TempDir()` for paths
- Use `filepath.Join()` instead of string concatenation
- Remove Unix `find` command dependency completely
- Add new `findVTTFiles()` method using `filepath.Walk()`
- Make VTT file discovery work on Windows
- Improve error handling for file operations
- Maintain backward compatibility with existing functionality
2025-06-11 22:24:48 -07:00
github-actions[bot]
f59e5081f3 Update version to v1.4.201 and commit 2025-06-12 02:35:09 +00:00
Eugen Eisler
6a504c7422 Merge pull request #1503 from danielmiessler/dependabot/npm_and_yarn/web/npm_and_yarn-6ea9762674
chore(deps): bump brace-expansion from 1.1.11 to 1.1.12 in /web in the npm_and_yarn group across 1 directory
2025-06-12 04:33:36 +02:00
Eugen Eisler
89a0abcbe4 Merge pull request #1508 from ksylvan/0611-youtube-followup-fixes
feat: cleanup after `yt-dlp` addition
2025-06-12 04:32:30 +02:00
Kayvan Sylvan
2dfd78ef0b feat: cleanup after yt-dlp addition
### CHANGES
- Update README with yt-dlp requirement for transcripts
- Ensure the errors are clear and actionable.
2025-06-11 17:27:11 -07:00
dependabot[bot]
02ac68834d chore(deps): bump brace-expansion
Bumps the npm_and_yarn group with 1 update in the /web directory: [brace-expansion](https://github.com/juliangruber/brace-expansion).


Updates `brace-expansion` from 1.1.11 to 1.1.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

---
updated-dependencies:
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-11 18:41:27 +00:00
40 changed files with 5073 additions and 512 deletions

View File

@@ -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: |

View File

@@ -13,9 +13,11 @@ Fabric is graciously supported by…
![GitHub last commit](https://img.shields.io/github/last-commit/danielmiessler/fabric)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](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) •
@@ -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]
> May 22, 2025
>
> - Fabric now supports Anthropic's Claude 4. Read the [blog post from Anthropic](https://www.anthropic.com/news/claude-4).
## 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

View File

@@ -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

View File

@@ -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,32 @@ 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 +94,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)

17
go.mod
View File

@@ -8,6 +8,8 @@ require (
github.com/anaskhan96/soup v1.2.5
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.1
github.com/go-git/go-git/v5 v5.16.2
@@ -39,6 +41,20 @@ require (
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/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
@@ -76,6 +92,7 @@ require (
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // 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

40
go.sum
View File

@@ -31,6 +31,44 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
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/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=
@@ -164,6 +202,8 @@ github.com/sashabaranov/go-openai v1.40.1/go.mod h1:lj5b/K+zjTSFxVLijLSTDZuP7adO
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
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=

View File

@@ -43,6 +43,54 @@ schema = 3
[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.3"
hash = "sha256-Nnt5b2NkIvSXhGERQmyI0ka28hbWi7A7Zn3dsAjPcEA="
@@ -190,6 +238,9 @@ schema = 3
[mod."github.com/sergi/go-diff"]
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="

View File

@@ -1 +1 @@
"1.4.200"
"1.4.211"

View File

@@ -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 100 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:

View File

@@ -0,0 +1,37 @@
# create_mnemonic_phrases
Generate short, memorable sentences that embed Dicewarestyle 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 sixsided dice** (1111166666) to a unique word. Because there are `6^5 = 7776` combinations, the canonical list contains the same number of entries.
### Entropy of the standard 7776word 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.925bits` of entropy—≈77.5bits for six words, ≈129bits for ten, and so on. Four or more words already outclass most humanmade 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 spacedrepetition 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 Dicewarecompatible list
shuf -n 5 diceware_wordlist.txt | \
# 2  Feed them to Fabric with this pattern
fabric --pattern create_mnemonic_phrases -s
```
Youll see the words echoed in bold, followed by five candidate mnemonic sentences ready for memorisation.

View 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

View File

@@ -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.

View File

@@ -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}}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

@@ -1 +0,0 @@
CONTENT:

View File

@@ -0,0 +1,188 @@
// 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/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"
)
// BedrockClient is a plugin to add support for Amazon Bedrock
type BedrockClient struct {
*plugins.PluginBase
runtimeClient *bedrockruntime.Client
controlPlaneClient *bedrock.Client
}
// NewClient returns a new Bedrock plugin client
func NewClient() (ret *BedrockClient) {
vendorName := "Bedrock"
ctx := context.TODO()
cfg, err := config.LoadDefaultConfig(ctx)
cfg.APIOptions = append(cfg.APIOptions, middleware.AddUserAgentKeyValue("aiosc", "fabric"))
if err != nil {
fmt.Printf("Unable to load AWS Config: %s\n", err)
}
runtimeClient := bedrockruntime.NewFromConfig(cfg)
controlPlaneClient := bedrock.NewFromConfig(cfg)
ret = &BedrockClient{
PluginBase: &plugins.PluginBase{
Name: vendorName,
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
},
runtimeClient: runtimeClient,
controlPlaneClient: controlPlaneClient,
}
return
}
// ListModels lists the models available for use with the Bedrock plugin
func (c *BedrockClient) ListModels() ([]string, error) {
models := []string{}
ctx := context.TODO()
foundationModels, err := c.controlPlaneClient.ListFoundationModels(ctx, &bedrock.ListFoundationModelsInput{})
if err != nil {
return nil, 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(context.TODO())
if err != nil {
return nil, 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) {
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.TODO(), &converseInput)
if err != nil {
fmt.Printf("Error conversing with Bedrock: %s\n", err)
return
}
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"
close(channel)
// Unused Events
case *types.ConverseStreamOutputMemberMessageStart,
*types.ConverseStreamOutputMemberContentBlockStart,
*types.ConverseStreamOutputMemberContentBlockStop,
*types.ConverseStreamOutputMemberMetadata:
default:
fmt.Printf("Error: Unknown stream event type: %T\n", 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 {
fmt.Printf("Error conversing with Bedrock: %s\n", err)
return "", err
}
responseText, _ := response.Output.(*types.ConverseOutputMemberMessage)
responseContentBlock := responseText.Value.Content[0]
text, _ := responseContentBlock.(*types.ContentBlockMemberText)
return text.Value, nil
}
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
}

View 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
}

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -1,7 +1,12 @@
// Package youtube provides YouTube video transcript and comment extraction functionality.
// This implementation relies on yt-dlp for reliable transcript extraction, which must be
// installed separately. The old YouTube API scraping methods have been removed due to
// YouTube's frequent changes and rate limiting.
//
// 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 (
@@ -13,6 +18,7 @@ import (
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
@@ -30,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),
}
@@ -49,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))
@@ -57,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)
@@ -106,15 +112,17 @@ func (o *YouTube) GrabTranscriptWithTimestamps(videoId string, language string)
return o.tryMethodYtDlpWithTimestamps(videoId, language)
}
func (o *YouTube) tryMethodYtDlp(videoId string, language string) (ret string, err error) {
// 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
}
// Create a temporary directory for yt-dlp output
tempDir := "/tmp/fabric-youtube-" + videoId
// 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
@@ -123,14 +131,19 @@ func (o *YouTube) tryMethodYtDlp(videoId string, language string) (ret string, e
// 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", language,
"--sub-lang", lang_match,
"--skip-download",
"--sub-format", "vtt",
"--quiet",
"--no-warnings",
"-o", tempDir+"/%(title)s.%(ext)s",
"-o", outputPath,
videoURL)
var stderr bytes.Buffer
@@ -141,99 +154,21 @@ func (o *YouTube) tryMethodYtDlp(videoId string, language string) (ret string, e
return
}
// Find the VTT file in the temp directory
cmd2 := exec.Command("find", tempDir, "-name", "*."+language+".vtt", "-type", "f")
var findOut bytes.Buffer
cmd2.Stdout = &findOut
if err = cmd2.Run(); err != nil {
err = fmt.Errorf("failed to find VTT file: %v", err)
return
// Find VTT files using cross-platform approach
vttFiles, err := o.findVTTFiles(tempDir, language)
if err != nil {
return "", err
}
vttFiles := strings.Split(strings.TrimSpace(findOut.String()), "\n")
if len(vttFiles) == 0 || vttFiles[0] == "" {
// Try without language suffix
cmd3 := exec.Command("find", tempDir, "-name", "*.vtt", "-type", "f")
cmd3.Stdout = &findOut
findOut.Reset()
if err = cmd3.Run(); err != nil {
err = fmt.Errorf("no VTT files found")
return
}
vttFiles = strings.Split(strings.TrimSpace(findOut.String()), "\n")
if len(vttFiles) == 0 || vttFiles[0] == "" {
err = fmt.Errorf("no VTT files found in temp directory")
return
}
}
return processVTTFileFunc(vttFiles[0])
}
return o.readAndCleanVTTFile(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) {
// 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
}
// Create a temporary directory for yt-dlp output
tempDir := "/tmp/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
cmd := exec.Command("yt-dlp",
"--write-auto-subs",
"--sub-lang", language,
"--skip-download",
"--sub-format", "vtt",
"--quiet",
"--no-warnings",
"-o", tempDir+"/%(title)s.%(ext)s",
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 the VTT file in the temp directory
cmd2 := exec.Command("find", tempDir, "-name", "*."+language+".vtt", "-type", "f")
var findOut bytes.Buffer
cmd2.Stdout = &findOut
if err = cmd2.Run(); err != nil {
err = fmt.Errorf("failed to find VTT file: %v", err)
return
}
vttFiles := strings.Split(strings.TrimSpace(findOut.String()), "\n")
if len(vttFiles) == 0 || vttFiles[0] == "" {
// Try without language suffix
cmd3 := exec.Command("find", tempDir, "-name", "*.vtt", "-type", "f")
cmd3.Stdout = &findOut
findOut.Reset()
if err = cmd3.Run(); err != nil {
err = fmt.Errorf("no VTT files found")
return
}
vttFiles = strings.Split(strings.TrimSpace(findOut.String()), "\n")
if len(vttFiles) == 0 || vttFiles[0] == "" {
err = fmt.Errorf("no VTT files found in temp directory")
return
}
}
return o.readAndFormatVTTWithTimestamps(vttFiles[0])
return o.tryMethodYtDlpInternal(videoId, language, o.readAndFormatVTTWithTimestamps)
}
func (o *YouTube) readAndCleanVTTFile(filename string) (ret string, err error) {
@@ -555,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

View File

@@ -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{

View 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.)

View File

@@ -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

View File

@@ -1,3 +1,3 @@
package main
var version = "v1.4.200"
var version = "v1.4.211"

66
web/pnpm-lock.yaml generated
View File

@@ -608,6 +608,9 @@ packages:
'@types/estree@1.0.7':
resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==}
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/hast@3.0.4':
resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
@@ -654,6 +657,11 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
acorn@8.15.0:
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
engines: {node: '>=0.4.0'}
hasBin: true
agent-base@6.0.2:
resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
engines: {node: '>= 6.0.0'}
@@ -746,11 +754,11 @@ packages:
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
engines: {node: '>=8'}
brace-expansion@1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
brace-expansion@1.1.12:
resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
brace-expansion@2.0.1:
resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
@@ -1019,16 +1027,16 @@ packages:
resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
eslint-scope@8.3.0:
resolution: {integrity: sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==}
eslint-scope@8.4.0:
resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint-visitor-keys@3.4.3:
resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
eslint-visitor-keys@4.2.0:
resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==}
eslint-visitor-keys@4.2.1:
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
eslint@9.17.0:
@@ -1047,8 +1055,8 @@ packages:
esm-env@1.2.2:
resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
espree@10.3.0:
resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
espree@10.4.0:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
espree@9.6.1:
@@ -2472,7 +2480,7 @@ snapshots:
dependencies:
ajv: 6.12.6
debug: 4.4.1
espree: 10.3.0
espree: 10.4.0
globals: 14.0.0
ignore: 5.3.2
import-fresh: 3.3.1
@@ -2747,6 +2755,8 @@ snapshots:
'@types/estree@1.0.7': {}
'@types/estree@1.0.8': {}
'@types/hast@3.0.4':
dependencies:
'@types/unist': 3.0.3
@@ -2782,8 +2792,14 @@ snapshots:
dependencies:
acorn: 8.14.1
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
acorn@8.14.1: {}
acorn@8.15.0: {}
agent-base@6.0.2:
dependencies:
debug: 4.4.1
@@ -2866,12 +2882,12 @@ snapshots:
binary-extensions@2.3.0: {}
brace-expansion@1.1.11:
brace-expansion@1.1.12:
dependencies:
balanced-match: 1.0.2
concat-map: 0.0.1
brace-expansion@2.0.1:
brace-expansion@2.0.2:
dependencies:
balanced-match: 1.0.2
@@ -3146,14 +3162,14 @@ snapshots:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-scope@8.3.0:
eslint-scope@8.4.0:
dependencies:
esrecurse: 4.3.0
estraverse: 5.3.0
eslint-visitor-keys@3.4.3: {}
eslint-visitor-keys@4.2.0: {}
eslint-visitor-keys@4.2.1: {}
eslint@9.17.0(jiti@1.21.7):
dependencies:
@@ -3167,16 +3183,16 @@ snapshots:
'@humanfs/node': 0.16.6
'@humanwhocodes/module-importer': 1.0.1
'@humanwhocodes/retry': 0.4.3
'@types/estree': 1.0.7
'@types/estree': 1.0.8
'@types/json-schema': 7.0.15
ajv: 6.12.6
chalk: 4.1.2
cross-spawn: 7.0.6
debug: 4.4.1
escape-string-regexp: 4.0.0
eslint-scope: 8.3.0
eslint-visitor-keys: 4.2.0
espree: 10.3.0
eslint-scope: 8.4.0
eslint-visitor-keys: 4.2.1
espree: 10.4.0
esquery: 1.6.0
esutils: 2.0.3
fast-deep-equal: 3.1.3
@@ -3200,11 +3216,11 @@ snapshots:
esm-env@1.2.2: {}
espree@10.3.0:
espree@10.4.0:
dependencies:
acorn: 8.14.1
acorn-jsx: 5.3.2(acorn@8.14.1)
eslint-visitor-keys: 4.2.0
acorn: 8.15.0
acorn-jsx: 5.3.2(acorn@8.15.0)
eslint-visitor-keys: 4.2.1
espree@9.6.1:
dependencies:
@@ -3710,11 +3726,11 @@ snapshots:
minimatch@3.1.2:
dependencies:
brace-expansion: 1.1.11
brace-expansion: 1.1.12
minimatch@9.0.5:
dependencies:
brace-expansion: 2.0.1
brace-expansion: 2.0.2
minimist@1.2.8: {}

View File

@@ -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 />

View File

@@ -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 {

View File

@@ -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();
}
}
}

View File

@@ -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),