mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-11 07:18:03 -05:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc8f5add00 | ||
|
|
c3f874f985 | ||
|
|
922df52d0c | ||
|
|
4badfecadb | ||
|
|
83139a64d5 | ||
|
|
78fd836532 | ||
|
|
894459ddec | ||
|
|
920c22c889 | ||
|
|
a0f931feb0 | ||
|
|
4b080fd6dd | ||
|
|
298abecb3f | ||
|
|
e2d4aab775 | ||
|
|
17cac13584 | ||
|
|
e4a004cf88 | ||
|
|
fcb10feadd | ||
|
|
9560537730 | ||
|
|
42fabab352 | ||
|
|
895ca1ad99 | ||
|
|
2ef7db8bb2 | ||
|
|
8491354a30 | ||
|
|
1fd5b0d27b | ||
|
|
7eb67ee82d |
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -24,6 +24,7 @@
|
||||
"compadd",
|
||||
"compdef",
|
||||
"compinit",
|
||||
"conceptmap",
|
||||
"creatordate",
|
||||
"curcontext",
|
||||
"custompatterns",
|
||||
@@ -95,6 +96,7 @@
|
||||
"joho",
|
||||
"kballard",
|
||||
"Keploy",
|
||||
"kimi",
|
||||
"Kore",
|
||||
"ksylvan",
|
||||
"Langdock",
|
||||
@@ -150,6 +152,7 @@
|
||||
"Pulcherrima",
|
||||
"pycache",
|
||||
"pyperclip",
|
||||
"qwen",
|
||||
"readystream",
|
||||
"restapi",
|
||||
"rmextension",
|
||||
|
||||
61
CHANGELOG.md
61
CHANGELOG.md
@@ -1,5 +1,66 @@
|
||||
# Changelog
|
||||
|
||||
## v1.4.339 (2025-12-08)
|
||||
|
||||
### PR [#1855](https://github.com/danielmiessler/Fabric/pull/1855) by [ksylvan](https://github.com/ksylvan): feat: add image attachment support for Ollama vision models
|
||||
|
||||
- Add multi-modal image support to Ollama client
|
||||
- Implement convertMessage to handle multi-content chat messages
|
||||
- Add loadImageBytes to fetch images from URLs
|
||||
- Support base64 data URLs for inline images
|
||||
- Handle HTTP image URLs with context propagation
|
||||
|
||||
## v1.4.338 (2025-12-04)
|
||||
|
||||
### PR [#1852](https://github.com/danielmiessler/Fabric/pull/1852) by [ksylvan](https://github.com/ksylvan): Add Abacus vendor for ChatLLM models with static model list
|
||||
|
||||
- Add static model support and register Abacus provider
|
||||
- Detect modelsURL starting with 'static:' and route appropriately
|
||||
- Implement getStaticModels returning curated Abacus model list
|
||||
- Register Abacus provider with ModelsURL 'static:abacus'
|
||||
- Extend provider tests to include Abacus existence
|
||||
|
||||
## v1.4.337 (2025-12-04)
|
||||
|
||||
### PR [#1851](https://github.com/danielmiessler/Fabric/pull/1851) by [ksylvan](https://github.com/ksylvan): Add Z AI provider and glm model support
|
||||
|
||||
- Add Z AI provider configuration to ProviderMap
|
||||
- Include BaseURL for Z AI API endpoint
|
||||
- Add test case for Z AI provider existence
|
||||
- Add glm to OpenAI model prefixes list
|
||||
- Support new Z AI provider in OpenAI compatible plugins
|
||||
|
||||
## v1.4.336 (2025-12-01)
|
||||
|
||||
### PR [#1848](https://github.com/danielmiessler/Fabric/pull/1848) by [zeddy303](https://github.com/zeddy303): Fix localStorage SSR error in favorites-store
|
||||
|
||||
- Fix localStorage SSR error in favorites-store by using SvelteKit's browser constant instead of typeof localStorage check to properly handle server-side rendering and prevent 'localStorage.getItem is not a function' error when running dev server
|
||||
|
||||
## v1.4.335 (2025-11-28)
|
||||
|
||||
### PR [#1847](https://github.com/danielmiessler/Fabric/pull/1847) by [ksylvan](https://github.com/ksylvan): Improve model name matching for NeedsRaw in Ollama plugin
|
||||
|
||||
- Improved model name matching in Ollama plugin by replacing prefix-based matching with substring matching
|
||||
- Enhanced NeedsRaw functionality to support more flexible model name detection
|
||||
- Renamed `ollamaPrefixes` variable to `ollamaSearchStrings` for better code clarity
|
||||
- Replaced `HasPrefix` function with `Contains` for more comprehensive model matching
|
||||
- Added "conceptmap" to VSCode dictionary settings
|
||||
|
||||
### Direct commits
|
||||
|
||||
- Merge branch 'danielmiessler:main' into main
|
||||
- Docs: Fix typo in README
|
||||
|
||||
## v1.4.334 (2025-11-26)
|
||||
|
||||
### PR [#1845](https://github.com/danielmiessler/Fabric/pull/1845) by [ksylvan](https://github.com/ksylvan): Add Claude Opus 4.5 Support
|
||||
|
||||
- Add Claude Opus 4.5 model variants to Anthropic client
|
||||
- Upgrade anthropic-sdk-go from v1.16.0 to v1.19.0
|
||||
- Update golang.org/x/crypto from v0.41.0 to v0.45.0
|
||||
- Upgrade golang.org/x/net from v0.43.0 to v0.47.0
|
||||
- Bump golang.org/x/text from v0.28.0 to v0.31.0
|
||||
|
||||
## v1.4.333 (2025-11-25)
|
||||
|
||||
### PR [#1833](https://github.com/danielmiessler/Fabric/pull/1833) by [junaid18183](https://github.com/junaid18183): Added concall_summary
|
||||
|
||||
@@ -73,6 +73,10 @@ Below are the **new features and capabilities** we've added (newest first):
|
||||
|
||||
### Recent Major Features
|
||||
|
||||
- [v1.4.338](https://github.com/danielmiessler/fabric/releases/tag/v1.4.338) (Dec 4, 2025) — Add Abacus vendor support for Chat-LLM
|
||||
models (see [RouteLLM APIs](https://abacus.ai/app/route-llm-apis)).
|
||||
- [v1.4.337](https://github.com/danielmiessler/fabric/releases/tag/v1.4.337) (Dec 4, 2025) — Add "Z AI" vendor support. See the [Z AI overview](https://docs.z.ai/guides/overview/overview) page for more details.
|
||||
- [v1.4.334](https://github.com/danielmiessler/fabric/releases/tag/v1.4.334) (Nov 26, 2025) — **Claude Opus 4.5**: Updates the Anthropic SDK to the latest and adds the new [Claude Opus 4.5](https://www.anthropic.com/news/claude-opus-4-5) to the available models.
|
||||
- [v1.4.331](https://github.com/danielmiessler/fabric/releases/tag/v1.4.331) (Nov 23, 2025) — **Support for GitHub Models**: Adds support for using GitHub Models.
|
||||
- [v1.4.322](https://github.com/danielmiessler/fabric/releases/tag/v1.4.322) (Nov 5, 2025) — **Interactive HTML Concept Maps and Claude Sonnet 4.5**: Adds `create_conceptmap` pattern for visual knowledge representation using Vis.js, introduces WELLNESS category with psychological analysis patterns, and upgrades to Claude Sonnet 4.5
|
||||
- [v1.4.317](https://github.com/danielmiessler/fabric/releases/tag/v1.4.317) (Sep 21, 2025) — **Portuguese Language Variants**: Adds BCP 47 locale normalization with support for Brazilian Portuguese (pt-BR) and European Portuguese (pt-PT) with intelligent fallback chains
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v1.4.333"
|
||||
var version = "v1.4.339"
|
||||
|
||||
Binary file not shown.
12
go.mod
12
go.mod
@@ -3,7 +3,7 @@ module github.com/danielmiessler/fabric
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/anthropics/anthropic-sdk-go v1.16.0
|
||||
github.com/anthropics/anthropic-sdk-go v1.19.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aws/aws-sdk-go-v2 v1.39.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.31.8
|
||||
@@ -29,7 +29,7 @@ require (
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/text v0.28.0
|
||||
golang.org/x/text v0.31.0
|
||||
google.golang.org/api v0.247.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -118,11 +118,11 @@ require (
|
||||
go.opentelemetry.io/otel/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
golang.org/x/arch v0.18.0 // indirect
|
||||
golang.org/x/crypto v0.41.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.35.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
google.golang.org/genai v1.17.0
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250818200422-3122310a409c // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250818200422-3122310a409c // indirect
|
||||
|
||||
13
go.sum
13
go.sum
@@ -29,6 +29,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/anthropics/anthropic-sdk-go v1.16.0 h1:nRkOFDqYXsHteoIhjdJr/5dsiKbFF3rflSv8ax50y8o=
|
||||
github.com/anthropics/anthropic-sdk-go v1.16.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
|
||||
github.com/anthropics/anthropic-sdk-go v1.19.0 h1:mO6E+ffSzLRvR/YUH9KJC0uGw0uV8GjISIuzem//3KE=
|
||||
github.com/anthropics/anthropic-sdk-go v1.19.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
@@ -290,6 +292,8 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4=
|
||||
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
@@ -309,6 +313,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -320,6 +326,8 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -338,6 +346,8 @@ golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -349,6 +359,7 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4=
|
||||
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
@@ -361,6 +372,8 @@ golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
|
||||
@@ -50,6 +50,8 @@ func NewClient() (ret *Client) {
|
||||
string(anthropic.ModelClaudeOpus4_1_20250805),
|
||||
string(anthropic.ModelClaudeSonnet4_5),
|
||||
string(anthropic.ModelClaudeSonnet4_5_20250929),
|
||||
string(anthropic.ModelClaudeOpus4_5_20251101),
|
||||
string(anthropic.ModelClaudeOpus4_5),
|
||||
}
|
||||
|
||||
ret.modelBetas = map[string][]string{
|
||||
|
||||
@@ -2,7 +2,9 @@ package ollama
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -10,11 +12,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/chat"
|
||||
ollamaapi "github.com/ollama/ollama/api"
|
||||
"github.com/samber/lo"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/domain"
|
||||
debuglog "github.com/danielmiessler/fabric/internal/log"
|
||||
"github.com/danielmiessler/fabric/internal/plugins"
|
||||
ollamaapi "github.com/ollama/ollama/api"
|
||||
)
|
||||
|
||||
const defaultBaseUrl = "http://localhost:11434"
|
||||
@@ -48,6 +49,7 @@ type Client struct {
|
||||
apiUrl *url.URL
|
||||
client *ollamaapi.Client
|
||||
ApiHttpTimeout *plugins.SetupQuestion
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
type transport_sec struct {
|
||||
@@ -84,7 +86,8 @@ func (o *Client) configure() (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
o.client = ollamaapi.NewClient(o.apiUrl, &http.Client{Timeout: timeout, Transport: &transport_sec{underlyingTransport: http.DefaultTransport, ApiKey: o.ApiKey}})
|
||||
o.httpClient = &http.Client{Timeout: timeout, Transport: &transport_sec{underlyingTransport: http.DefaultTransport, ApiKey: o.ApiKey}}
|
||||
o.client = ollamaapi.NewClient(o.apiUrl, o.httpClient)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -104,15 +107,18 @@ func (o *Client) ListModels() (ret []string, err error) {
|
||||
}
|
||||
|
||||
func (o *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan string) (err error) {
|
||||
req := o.createChatRequest(msgs, opts)
|
||||
ctx := context.Background()
|
||||
|
||||
var req ollamaapi.ChatRequest
|
||||
if req, err = o.createChatRequest(ctx, msgs, opts); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
respFunc := func(resp ollamaapi.ChatResponse) (streamErr error) {
|
||||
channel <- resp.Message.Content
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
if err = o.client.Chat(ctx, &req, respFunc); err != nil {
|
||||
return
|
||||
}
|
||||
@@ -124,7 +130,10 @@ func (o *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.Cha
|
||||
func (o *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions) (ret string, err error) {
|
||||
bf := false
|
||||
|
||||
req := o.createChatRequest(msgs, opts)
|
||||
var req ollamaapi.ChatRequest
|
||||
if req, err = o.createChatRequest(ctx, msgs, opts); err != nil {
|
||||
return
|
||||
}
|
||||
req.Stream = &bf
|
||||
|
||||
respFunc := func(resp ollamaapi.ChatResponse) (streamErr error) {
|
||||
@@ -133,15 +142,18 @@ func (o *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
||||
}
|
||||
|
||||
if err = o.client.Chat(ctx, &req, respFunc); err != nil {
|
||||
fmt.Printf("FRED --> %s\n", err)
|
||||
debuglog.Debug(debuglog.Basic, "Ollama chat request failed: %v\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) createChatRequest(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions) (ret ollamaapi.ChatRequest) {
|
||||
messages := lo.Map(msgs, func(message *chat.ChatCompletionMessage, _ int) (ret ollamaapi.Message) {
|
||||
return ollamaapi.Message{Role: message.Role, Content: message.Content}
|
||||
})
|
||||
func (o *Client) createChatRequest(ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions) (ret ollamaapi.ChatRequest, err error) {
|
||||
messages := make([]ollamaapi.Message, len(msgs))
|
||||
for i, message := range msgs {
|
||||
if messages[i], err = o.convertMessage(ctx, message); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
options := map[string]interface{}{
|
||||
"temperature": opts.Temperature,
|
||||
@@ -162,14 +174,85 @@ func (o *Client) createChatRequest(msgs []*chat.ChatCompletionMessage, opts *dom
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) convertMessage(ctx context.Context, message *chat.ChatCompletionMessage) (ret ollamaapi.Message, err error) {
|
||||
ret = ollamaapi.Message{Role: message.Role, Content: message.Content}
|
||||
|
||||
if len(message.MultiContent) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Pre-allocate with capacity hint
|
||||
textParts := make([]string, 0, len(message.MultiContent))
|
||||
if strings.TrimSpace(ret.Content) != "" {
|
||||
textParts = append(textParts, strings.TrimSpace(ret.Content))
|
||||
}
|
||||
|
||||
for _, part := range message.MultiContent {
|
||||
switch part.Type {
|
||||
case chat.ChatMessagePartTypeText:
|
||||
if trimmed := strings.TrimSpace(part.Text); trimmed != "" {
|
||||
textParts = append(textParts, trimmed)
|
||||
}
|
||||
case chat.ChatMessagePartTypeImageURL:
|
||||
// Nil guard
|
||||
if part.ImageURL == nil || part.ImageURL.URL == "" {
|
||||
continue
|
||||
}
|
||||
var img []byte
|
||||
if img, err = o.loadImageBytes(ctx, part.ImageURL.URL); err != nil {
|
||||
return
|
||||
}
|
||||
ret.Images = append(ret.Images, ollamaapi.ImageData(img))
|
||||
}
|
||||
}
|
||||
|
||||
ret.Content = strings.Join(textParts, "\n")
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) loadImageBytes(ctx context.Context, imageURL string) (ret []byte, err error) {
|
||||
// Handle data URLs (base64 encoded)
|
||||
if strings.HasPrefix(imageURL, "data:") {
|
||||
parts := strings.SplitN(imageURL, ",", 2)
|
||||
if len(parts) != 2 {
|
||||
err = fmt.Errorf("invalid data URL format")
|
||||
return
|
||||
}
|
||||
if ret, err = base64.StdEncoding.DecodeString(parts[1]); err != nil {
|
||||
err = fmt.Errorf("failed to decode data URL: %w", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Handle HTTP URLs with context
|
||||
var req *http.Request
|
||||
if req, err = http.NewRequestWithContext(ctx, http.MethodGet, imageURL, nil); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var resp *http.Response
|
||||
if resp, err = o.httpClient.Do(req); err != nil {
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
err = fmt.Errorf("failed to fetch image %s: %s", imageURL, resp.Status)
|
||||
return
|
||||
}
|
||||
|
||||
ret, err = io.ReadAll(resp.Body)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Client) NeedsRawMode(modelName string) bool {
|
||||
ollamaPrefixes := []string{
|
||||
ollamaSearchStrings := []string{
|
||||
"llama3",
|
||||
"llama2",
|
||||
"mistral",
|
||||
}
|
||||
for _, prefix := range ollamaPrefixes {
|
||||
if strings.HasPrefix(modelName, prefix) {
|
||||
for _, searchString := range ollamaSearchStrings {
|
||||
if strings.Contains(modelName, searchString) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,10 +172,11 @@ func (o *Client) supportsResponsesAPI() bool {
|
||||
|
||||
func (o *Client) NeedsRawMode(modelName string) bool {
|
||||
openaiModelsPrefixes := []string{
|
||||
"glm",
|
||||
"gpt-5",
|
||||
"o1",
|
||||
"o3",
|
||||
"o4",
|
||||
"gpt-5",
|
||||
}
|
||||
openAIModelsNeedingRaw := []string{
|
||||
"gpt-4o-mini-search-preview",
|
||||
|
||||
@@ -2,6 +2,7 @@ package openai_compatible
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
@@ -38,8 +39,12 @@ func NewClient(providerConfig ProviderConfig) *Client {
|
||||
|
||||
// ListModels overrides the default ListModels to handle different response formats
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
// If a custom models URL is provided, use direct fetch with that URL
|
||||
// If a custom models URL is provided, handle it
|
||||
if c.modelsURL != "" {
|
||||
// Check for static model list
|
||||
if strings.HasPrefix(c.modelsURL, "static:") {
|
||||
return c.getStaticModels(c.modelsURL)
|
||||
}
|
||||
// TODO: Handle context properly in Fabric by accepting and propagating a context.Context
|
||||
// instead of creating a new one here.
|
||||
return openai.FetchModelsDirectly(context.Background(), c.modelsURL, c.Client.ApiKey.Value, c.GetName())
|
||||
@@ -55,6 +60,68 @@ func (c *Client) ListModels() ([]string, error) {
|
||||
return c.DirectlyGetModels(context.Background())
|
||||
}
|
||||
|
||||
// getStaticModels returns a predefined list of models for providers that don't support model discovery
|
||||
func (c *Client) getStaticModels(modelsKey string) ([]string, error) {
|
||||
switch modelsKey {
|
||||
case "static:abacus":
|
||||
return []string{
|
||||
"route-llm",
|
||||
"gpt-4o-2024-11-20",
|
||||
"gpt-4o-mini",
|
||||
"o4-mini",
|
||||
"o3-pro",
|
||||
"o3",
|
||||
"o3-mini",
|
||||
"gpt-4.1",
|
||||
"gpt-4.1-mini",
|
||||
"gpt-4.1-nano",
|
||||
"gpt-5",
|
||||
"gpt-5-mini",
|
||||
"gpt-5-nano",
|
||||
"gpt-5.1",
|
||||
"gpt-5.1-chat-latest",
|
||||
"openai/gpt-oss-120b",
|
||||
"claude-3-7-sonnet-20250219",
|
||||
"claude-sonnet-4-20250514",
|
||||
"claude-opus-4-20250514",
|
||||
"claude-opus-4-1-20250805",
|
||||
"claude-sonnet-4-5-20250929",
|
||||
"claude-haiku-4-5-20251001",
|
||||
"claude-opus-4-5-20251101",
|
||||
"meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
|
||||
"meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
|
||||
"meta-llama/Meta-Llama-3.1-70B-Instruct",
|
||||
"meta-llama/Meta-Llama-3.1-8B-Instruct",
|
||||
"llama-3.3-70b-versatile",
|
||||
"gemini-2.0-flash-001",
|
||||
"gemini-2.0-pro-exp-02-05",
|
||||
"gemini-2.5-pro",
|
||||
"gemini-2.5-flash",
|
||||
"gemini-3-pro-preview",
|
||||
"qwen-2.5-coder-32b",
|
||||
"Qwen/Qwen2.5-72B-Instruct",
|
||||
"Qwen/QwQ-32B",
|
||||
"Qwen/Qwen3-235B-A22B-Instruct-2507",
|
||||
"Qwen/Qwen3-32B",
|
||||
"qwen/qwen3-coder-480b-a35b-instruct",
|
||||
"qwen/qwen3-Max",
|
||||
"grok-4-0709",
|
||||
"grok-4-fast-non-reasoning",
|
||||
"grok-4-1-fast-non-reasoning",
|
||||
"grok-code-fast-1",
|
||||
"kimi-k2-turbo-preview",
|
||||
"deepseek/deepseek-v3.1",
|
||||
"deepseek-ai/DeepSeek-V3.1-Terminus",
|
||||
"deepseek-ai/DeepSeek-R1",
|
||||
"deepseek-ai/DeepSeek-V3.2",
|
||||
"zai-org/glm-4.5",
|
||||
"zai-org/glm-4.6",
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown static model list: %s", modelsKey)
|
||||
}
|
||||
}
|
||||
|
||||
// ProviderMap is a map of provider name to ProviderConfig for O(1) lookup
|
||||
var ProviderMap = map[string]ProviderConfig{
|
||||
"AIML": {
|
||||
@@ -123,6 +190,17 @@ var ProviderMap = map[string]ProviderConfig{
|
||||
BaseURL: "https://api.venice.ai/api/v1",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"Z AI": {
|
||||
Name: "Z AI",
|
||||
BaseURL: "https://api.z.ai/api/paas/v4",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"Abacus": {
|
||||
Name: "Abacus",
|
||||
BaseURL: "https://routellm.abacus.ai/v1/",
|
||||
ModelsURL: "static:abacus", // Special marker for static model list
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
}
|
||||
|
||||
// GetProviderByName returns the provider configuration for a given name with O(1) lookup
|
||||
|
||||
@@ -20,6 +20,16 @@ func TestCreateClient(t *testing.T) {
|
||||
provider: "Groq",
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: "Existing provider - Z AI",
|
||||
provider: "Z AI",
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: "Existing provider - Abacus",
|
||||
provider: "Abacus",
|
||||
exists: true,
|
||||
},
|
||||
{
|
||||
name: "Non-existent provider",
|
||||
provider: "NonExistent",
|
||||
|
||||
@@ -32,8 +32,8 @@ schema = 3
|
||||
version = "v1.3.3"
|
||||
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
|
||||
[mod."github.com/anthropics/anthropic-sdk-go"]
|
||||
version = "v1.16.0"
|
||||
hash = "sha256-hD6Ix+V5IBFfoaCuAZemrDQx/+G111fCYHn2FAxFuEE="
|
||||
version = "v1.19.0"
|
||||
hash = "sha256-ubYeau5XL0tx4c/79L58rzJGOdOWs9z6WQOtN6mpgxw="
|
||||
[mod."github.com/araddon/dateparse"]
|
||||
version = "v0.0.0-20210429162001-6b43995a97de"
|
||||
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
|
||||
@@ -317,26 +317,26 @@ schema = 3
|
||||
version = "v0.18.0"
|
||||
hash = "sha256-tUpUPERjmRi7zldj0oPlnbnBhEkcI9iQGvP1HqlsK10="
|
||||
[mod."golang.org/x/crypto"]
|
||||
version = "v0.41.0"
|
||||
hash = "sha256-o5Di0lsFmYnXl7a5MBTqmN9vXMCRpE9ay71C1Ar8jEY="
|
||||
version = "v0.45.0"
|
||||
hash = "sha256-IpNesJYxFcs2jGvagwJrUD/gsJfA3UiETjQwYByXxSY="
|
||||
[mod."golang.org/x/exp"]
|
||||
version = "v0.0.0-20250531010427-b6e5de432a8b"
|
||||
hash = "sha256-QaFfjyB+pogCkUkJskR9xnXwkCOU828XJRrzwwLm6Ms="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.43.0"
|
||||
hash = "sha256-bf3iQFrsC8BoarVaS0uSspEFAcr1zHp1uziTtBpwV34="
|
||||
version = "v0.47.0"
|
||||
hash = "sha256-2qFgCd0YfNCGkLrf+xvnhQtKjSe8CymMdLlN3svUYTg="
|
||||
[mod."golang.org/x/oauth2"]
|
||||
version = "v0.30.0"
|
||||
hash = "sha256-btD7BUtQpOswusZY5qIU90uDo38buVrQ0tmmQ8qNHDg="
|
||||
[mod."golang.org/x/sync"]
|
||||
version = "v0.16.0"
|
||||
hash = "sha256-sqKDRESeMzLe0jWGWltLZL/JIgrn0XaIeBWCzVN3Bks="
|
||||
version = "v0.18.0"
|
||||
hash = "sha256-S8o6y7GOaYWeq+TzT8BB6T+1mg82Mu08V0TL3ukJprg="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.35.0"
|
||||
hash = "sha256-ZKM8pesQE6NAFZeKQ84oPn5JMhGr8g4TSwLYAsHMGSI="
|
||||
version = "v0.38.0"
|
||||
hash = "sha256-1+i5EaG3JwH3KMtefzJLG5R6jbOeJM4GK3/LHBVnSy0="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.28.0"
|
||||
hash = "sha256-8UlJniGK+km4Hmrw6XMxELnExgrih7+z8tU26Cntmto="
|
||||
version = "v0.31.0"
|
||||
hash = "sha256-AT46RrSmV6+/d5FDhs9fPwYzmQ7WSo+YL9tPfhREwLw="
|
||||
[mod."google.golang.org/api"]
|
||||
version = "v0.247.0"
|
||||
hash = "sha256-UzTtydHmNqh1OXbxcN5qNKQxb5dV6h2Mo6DH4P219Ec="
|
||||
|
||||
@@ -1 +1 @@
|
||||
"1.4.333"
|
||||
"1.4.339"
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { writable } from 'svelte/store';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
// Load favorites from localStorage if available
|
||||
const storedFavorites = typeof localStorage !== 'undefined'
|
||||
const storedFavorites = browser
|
||||
? JSON.parse(localStorage.getItem('favoritePatterns') || '[]')
|
||||
: [];
|
||||
|
||||
const createFavoritesStore = () => {
|
||||
const { subscribe, set, update } = writable<string[]>(storedFavorites);
|
||||
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
toggleFavorite: (patternName: string) => {
|
||||
@@ -17,7 +18,7 @@ const createFavoritesStore = () => {
|
||||
: [...favorites, patternName];
|
||||
|
||||
// Save to localStorage
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
if (browser) {
|
||||
localStorage.setItem('favoritePatterns', JSON.stringify(newFavorites));
|
||||
}
|
||||
|
||||
@@ -26,11 +27,11 @@ const createFavoritesStore = () => {
|
||||
},
|
||||
reset: () => {
|
||||
set([]);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
if (browser) {
|
||||
localStorage.removeItem('favoritePatterns');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
export const favorites = createFavoritesStore();
|
||||
export const favorites = createFavoritesStore();
|
||||
|
||||
Reference in New Issue
Block a user