mirror of
https://github.com/danielmiessler/fabric.git
synced 2026-09-10 07:36:44 -04:00
feat: cache OpenAI model discovery and handle provider rate limits
- Add persistent cache for provider model discovery results - Serve stale model caches during discovery failures - Return concise localized errors for rate-limited model fetches - Send GitHub Models API version header automatically - Add Claude Fable 5 Anthropic model support - Omit sampling parameters for Claude Fable 5 - Update model rate-limit translations across supported locales - Add tests for cache, rate-limit, and GitHub headers - Update Go dependencies for AI provider integrations
This commit is contained in:
parent
29b32f9ff2
commit
59d26a7e89
90
go.mod
90
go.mod
|
|
@ -4,12 +4,12 @@ go 1.26.0
|
|||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
|
||||
github.com/anthropics/anthropic-sdk-go v1.46.0
|
||||
github.com/anthropics/anthropic-sdk-go v1.49.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.8
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.19
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.62.0
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.42.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.24
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.63.4
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.5
|
||||
github.com/gabriel-vasile/mimetype v1.4.13
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/go-git/go-git/v5 v5.19.1
|
||||
|
|
@ -19,9 +19,9 @@ require (
|
|||
github.com/jessevdk/go-flags v1.6.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/mattn/go-sqlite3 v1.14.44
|
||||
github.com/mattn/go-sqlite3 v1.14.45
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1
|
||||
github.com/ollama/ollama v0.24.0
|
||||
github.com/ollama/ollama v0.30.7
|
||||
github.com/openai/openai-go v1.12.0
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
|
|
@ -33,48 +33,50 @@ require (
|
|||
github.com/swaggo/gin-swagger v1.6.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/text v0.37.0
|
||||
google.golang.org/api v0.282.0
|
||||
golang.org/x/text v0.38.0
|
||||
google.golang.org/api v0.283.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.5 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.2.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/spec v0.22.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/loading v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.6 // indirect
|
||||
github.com/go-openapi/spec v0.22.5 // indirect
|
||||
github.com/go-openapi/swag/conv v0.26.1 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.26.1 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.26.1 // indirect
|
||||
github.com/go-openapi/swag/loading v0.26.1 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.26.1 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.26.1 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.26.1 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/invopop/jsonschema v0.13.0 // indirect
|
||||
github.com/invopop/jsonschema v0.14.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.2 // indirect
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.1 // indirect
|
||||
github.com/quic-go/quic-go v0.60.0 // indirect
|
||||
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.5 // indirect
|
||||
golang.org/x/mod v0.37.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
)
|
||||
|
|
@ -87,20 +89,20 @@ require (
|
|||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.4 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.18
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.24 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.18 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 // indirect
|
||||
github.com/aws/smithy-go v1.26.0 // indirect
|
||||
github.com/bytedance/sonic v1.15.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.23
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 // indirect
|
||||
github.com/aws/smithy-go v1.27.2 // indirect
|
||||
github.com/bytedance/sonic v1.15.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||
|
|
@ -116,7 +118,7 @@ require (
|
|||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.2 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.3 // indirect
|
||||
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||
|
|
@ -154,13 +156,13 @@ require (
|
|||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
golang.org/x/arch v0.27.0 // indirect
|
||||
golang.org/x/crypto v0.52.0 // indirect
|
||||
golang.org/x/arch v0.28.0 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
google.golang.org/genai v1.58.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
google.golang.org/genai v1.59.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260608224507-4308a22a1bab // indirect
|
||||
google.golang.org/grpc v1.81.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
|
|
|
|||
96
go.sum
96
go.sum
|
|
@ -10,6 +10,8 @@ dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
|||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 h1:jHb/wfvRikGdxMXYV3QG/SzUOPYN9KEUUuC0Yd0/vC0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1/go.mod h1:pzBXCYn05zvYIrwLgtK8Ap8QcjRg+0i76tMQdWN6wOk=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 h1:aokoqcHvaGjiM3VpjKDfMMnF/8epJ+Q1HLJ7CudztqE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0/go.mod h1:/WYEx9pcM9Y+Dd/APJaNlSvVSvzl54rrMdZT5+Oi2LM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
|
|
@ -31,10 +33,14 @@ github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67
|
|||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg=
|
||||
github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/anthropics/anthropic-sdk-go v1.46.0 h1:yl3n+el5ZfNgiCtQ7zQ7s/NXxB11YbrKXdc3uLPNWlU=
|
||||
github.com/anthropics/anthropic-sdk-go v1.46.0/go.mod h1:bx5vWuHFuGPkELH8Z4KUiNSohFnUwScdpTyr+50myPo=
|
||||
github.com/anthropics/anthropic-sdk-go v1.49.0 h1:7maVcwOXaNGBkQC3Mi7CxWlUvrReZzXJPpeN94Rre/o=
|
||||
github.com/anthropics/anthropic-sdk-go v1.49.0/go.mod h1:3EfIfmFqxH6rbiLcIP4tPFyXL/IHakx2wDG4OU+TIEI=
|
||||
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=
|
||||
|
|
@ -43,38 +49,72 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
|
|||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.8 h1:sRs7nG6/RiEBZ/K5UO2sNw0w40U02Nmz1VtARloTZXk=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.8/go.mod h1:4LAfZOPHNVNQEckOACQx60Y8pSRjIkNZQz1w92xpMJc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA=
|
||||
github.com/aws/aws-sdk-go-v2 v1.42.0/go.mod h1:27+ACypSLljLAEKsCYOmrjKh83vuTRkuAe9Uv/3A4bg=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10 h1:gx1AwW1Iyk9Z9dD9F4akX5gnN3QZwUB20GGKH/I+Rho=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.10/go.mod h1:qqY157uZoqm5OXq/amuaBJyC9hgBCBQnsaWnPe905GY=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 h1:p1BBrg/Hhp6uK7zpejeI8QFXHJeC/mynzi04Sl03k9g=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13/go.mod h1:8cIfkE9MDhkRZGpQ22aV6/lkYeYSozpz16Smrs5x4Ls=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.19 h1:qRhIJMbevHUvIE7X4TK8N8zye5+5AhapcslPrvB+qKE=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.19/go.mod h1:RbJ24nfoya63+Mf5VI+CGCGk9vEdv28xPeii+gojRYs=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.24 h1:aEDEj533yGdVvEHfkCY0D/1FbDrjnZr4pIulxRjqpHs=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.24/go.mod h1:yZtrGKJGlqfEW+/m2uTsJK+Jz7xF5R0eZfgcIG9m1ss=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.18 h1:GcXQz2M/0ZvMo0v5DakUqbDBeBM1ZNaivkolEF4Esgw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.18/go.mod h1:sHJ06tMGcD3ZpmMyJqV+VBsGilhSIZPIN+ZFy5Dg0C4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.23 h1:Zhu3GOpRCkNjtE/gJpuPDsytSnaCCTQk8neAGsgzG5Y=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.23/go.mod h1:VsJF2ropPB37gDr7M2rLSpCE8IQWdpl62uae7qxZmqU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24 h1:FQm5ApnyzkuJdXLGskPce83CK1CQKC4RUnIHKVe4BU4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.24/go.mod h1:JsC7dqQc55MlZ5mvNsDMMge71u8pVcSzU3RNz2h/5yQ=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 h1:r6qZHbT+wxgWO/e9vYNUEtg7lv5+UN3pRqKhLXvnArg=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29/go.mod h1:QRnaRcTVGKPGRy8w78HMQtKUGRYcnMZAANATkeVA6Mo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24 h1:u6kJU2i0va1AgtJsH3RdWKWqHULlTh7zHwb35Womf74=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.24/go.mod h1:7GY+xLcXOFUpCkNwDReft9qOAVg54A4/AnjHIU7sSAY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29 h1:f3vKqSo13fhTYb+JEcXwXefZQE26I1FB5eTSniU67ko=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.29/go.mod h1:MzoLFUArKGpGD+ukmPiTPG1X5x4o6M2kq4v2dr1FiEc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24 h1:Xhbcf3KugX6vX7SDyUK205Oicyfg7EGuvoVNyP5L6DM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.24/go.mod h1:rwDgb2HNOGZsnTHylOUedM7Vnl+bCfnXDqUNPsFWYfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29 h1:RdwIf/CuUsvJX3RgJagbOyotl/cxoLY4xviKuE7p2GY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.29/go.mod h1:71wt8W2EgswdZy9Mf9KNnzxZ3TiZlv4caKghPktDOkA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25 h1:54CTMmlJ71Rk2dYvM9qZOob+39wjlVja2zDLxCu69Ew=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.25/go.mod h1:BZaHqxsS9vN1fvV5EfEl0OBLOk5+AajWsMu6MjqnZB4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30 h1:VTGy885W5DKBxWRUJbym9hytNaYzsyaPkCHGRRMAOhU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.30/go.mod h1:AS0HycUvJRFvTt613AYDOgO2jzw+00cVSMny8XB3yMY=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.62.0 h1:W+9SC9IbSJfB8uIVvYrE4+AR5Id47q6uma7zKcpoHMc=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.62.0/go.mod h1:s8wtBVt3pTWX6N4QvrYzOehkoUsVzkiGRdZuA4utdVQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.63.4 h1:i/+Jh9Ltiti6JqSEWC+3wcSL89Zrg9ekR84B8keIdX4=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.63.4/go.mod h1:f0MnAznWRN75Dnezt6MBnHOKHEpAI/ztr4Q6Lo1IGDk=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.0 h1:lVEyHJxhwEkoOM6w/Yf4BTqTgTLjxvQwZRTizsrAtjo=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.0/go.mod h1:lmxJQ2g6R0hrjXgLPdvpa39NlYkmM9xnzg9XOTLxIK0=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.5 h1:gWz8Ax1W8BXOoWe3obsIF4ueAY39u9A+NzsY4jppoIw=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.53.5/go.mod h1:8m0vIhh44Mmgb+x5o2WzTt0T5NKVtTBhO1j+t7AyvJI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9 h1:FLudkZLt5ci0ozzgkVo8BJGwvqNaZbTWb3UcucAateA=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.9/go.mod h1:w7wZ/s9qK7c8g4al+UyoF1Sp/Z45UwMGcqIzLWVQHWk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12 h1:ZD2+BSw9vFsNlKYIasSNt3uDbjqqXIBcM13UJv/Lx2k=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.12/go.mod h1:Ms4zlcVBbXbiP7EVLhl+lgjvA/a7YphqQ3Ih3174EmI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.24 h1:CQW2FTrflfoslYWLf3fv7vG28Q219+v8YJS5QTQb2+Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.24/go.mod h1:Xfx13T+u3nH6EEzgl9fBSO6nDRmze1FvnZNYkctQ2zw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29 h1:DRebniUGZ2MqiiIVmQJ04vIXr918hubdHMnarSLEWyU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.29/go.mod h1:LfRkPCD8YHDM2E5eTkos2UpwYeZnBcVarTa8L59bJHA=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.0 h1:yQo3eZ5qFaL1sJWqs1nL6j3yPHA2/R7c6tQ4T+0IO10=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.0/go.mod h1:3Zzou41Qt/ueXfIzHvTEjDNuR5IjCUBVF01SNhrt1e8=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.5 h1:6Xt6Ztjkwdia/7EtEaG7ki/qZUYlCcd7tGUotQed1QE=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.1.5/go.mod h1:LxYujSTLPRlp2vTtcUO/+1ilrew8ytt6SvQyOgejzFQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.18 h1:ApLTFdAZfDhZSiY5uskwECKHkSNNF83y2Ru2r7SezWA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.18/go.mod h1:A9K9qx2l6nK89hp+a350FdGfRkrkH5HdiEjHbiy/Q/c=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3 h1:ey1XLTYXb9PcLt4535632o5kCGXNXEhNb620Dqwuylo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.31.3/go.mod h1:Lk7PlmoTYryQmyBG0EXqj5BcUbj3whXdU2s3yGI3EAc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1 h1:4VD7TIZOGzehrgQ8vDE+1c6BQW4ErZPGY8ohZT5LXEE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.1/go.mod h1:er0SFJfdV89Rit5hIJu/EXtv+qC2XMnxoksLmcUFkqM=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6 h1:yLr03zQE/5Eu5l3QU0Si+xMbLMbSDF2YXsigqXngs6g=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.36.6/go.mod h1:Q5N6icH+KJZDLh+ESNwzdv6cZ6vLFF/egy3IOxWhmz4=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2 h1:XKnxlM4KZH1gktcsh3zSWc7GW4KivEv/OkifmHOhCUY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.42.2/go.mod h1:KJYmkQaFB3SUW2j3aBkPsxNmAb4ZsSOvbvCpuxzHJA0=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3 h1:VrIhKRCSK1umelSgB9RghvA9RTUYeQffyAS5ApXehNI=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.43.3/go.mod h1:r8wkDOuLaaMFqFiYAb8dGY2A3gJCOujMc6CFOVC4Zhc=
|
||||
github.com/aws/smithy-go v1.26.0 h1:9ouqbi+NyKP7fV3Te7UElCwdAb6Y8uk7LGwPE5tVe/s=
|
||||
github.com/aws/smithy-go v1.26.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aws/smithy-go v1.27.2 h1:y9NPmSE6am6LjEFPfqHqG/jJk7AauQvhCJONKh7kpzk=
|
||||
github.com/aws/smithy-go v1.27.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.2.0 h1:4EFcvK1kD4jyj6YqNK6skK6w+y7FHHBR+XBCtxwu/6g=
|
||||
|
|
@ -83,6 +123,8 @@ github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM
|
|||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.1 h1:nJD5PmM0vY7J8CT6MxoqbVAAMhkSmV2HgRAUrrpLoOw=
|
||||
github.com/bytedance/sonic v1.15.1/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
|
||||
github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo=
|
||||
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
|
||||
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
|
||||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
|
|
@ -142,29 +184,50 @@ github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0r
|
|||
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
|
||||
github.com/go-openapi/jsonreference v0.21.6 h1:NZ5nGfnaM1n4I43Xjm1e5/M2GjOwQwndQz22uhxwD+Y=
|
||||
github.com/go-openapi/jsonreference v0.21.6/go.mod h1:xzbgtQ3ZbWxvET3AxdzCJlJt6vkovbf+IfSPJjD0tUY=
|
||||
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
|
||||
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
|
||||
github.com/go-openapi/spec v0.22.5 h1:KhO7RBlKQfonUWX2WzQCoLIXVA6AcNqDGZ3a1Dutdlo=
|
||||
github.com/go-openapi/spec v0.22.5/go.mod h1:vxpOtMya5TXtENXKE5bKqv5NjocVhyhxHrlZfvKnZ74=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=
|
||||
github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=
|
||||
github.com/go-openapi/swag/conv v0.26.1 h1:slr5FVkg9Wc3Y5zcwenD8Sd/PQ94b2I/QJI7N7KTBpg=
|
||||
github.com/go-openapi/swag/conv v0.26.1/go.mod h1:mvQXgPptZk9GTrFgGwWvT4q+dN+zQej9JfmGwnipz1A=
|
||||
github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w=
|
||||
github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M=
|
||||
github.com/go-openapi/swag/jsonname v0.26.1 h1:VReupaV6WxlAsCn0e4DUfgV6bPmINnPpyJDLqSfNPcE=
|
||||
github.com/go-openapi/swag/jsonname v0.26.1/go.mod h1:OvdW6BoWoj33pTfi7x9vFrgmT+fk7aw0BRwvCE0YOuc=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.1 h1:2hdBfFkHg+7Wrz2VsCbeyR6hzkRDs7AztnMR2u84yOY=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.1/go.mod h1:U+RMJH3wa+6BRiphuRtIyI8fW9HPFqFQ4sHk2oRx0UQ=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.1 h1:1CD7NiLLb/TXl3tOnFYU4b+mNfb5rtgHkaA+q7RMYYQ=
|
||||
github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko=
|
||||
github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg=
|
||||
github.com/go-openapi/swag/loading v0.26.1 h1:E9K4wqXeROlhjFQ13K9zMz6ojFGXIggGe+ad1odrK9w=
|
||||
github.com/go-openapi/swag/loading v0.26.1/go.mod h1:3qvRIlWzWdq1HvmldwmuJ2ohpcAryN6xVt2OTKd0/7E=
|
||||
github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg=
|
||||
github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE=
|
||||
github.com/go-openapi/swag/stringutils v0.26.1 h1:f88uYyTso7TnHrKM/bUBsQ5e2wKf37cpgo6pvbzd9yU=
|
||||
github.com/go-openapi/swag/stringutils v0.26.1/go.mod h1:Sc6d3bU8fgk5AyZR8/8jEQ+Is/Ald+TD/IIggPN8UJk=
|
||||
github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4=
|
||||
github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=
|
||||
github.com/go-openapi/swag/typeutils v0.26.1 h1:yg42FgMzRR6PVQ3M3qHz1s+Y6/P4HoJ3cBarXa3OVnU=
|
||||
github.com/go-openapi/swag/typeutils v0.26.1/go.mod h1:VfnV+oUtSP2vCSCn2aJgnr8OevUYemyIzzS1VOzS10o=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.1 h1:0TSLK+lXs9vfIhAWzBeI/lOzEnIoot6WTCO1aAeWFTk=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.1/go.mod h1:7W5b7PRX9MxwL7TjeG7H8HkyBGRsIDRObhyMWFgBI2M=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2 h1:5zRca5jw7lzVREKCZVNBpysDNBjj74rBh0N2BGQbSR0=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.2/go.mod h1:XVevPw5hUXuV+5AkI1u1PeAm27EQVrhXTTCPAF85LmE=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8=
|
||||
github.com/go-openapi/testify/v2 v2.4.2 h1:tiByHpvE9uHrrKjOszax7ZvKB7QOgizBWGBLuq0ePx4=
|
||||
github.com/go-openapi/testify/v2 v2.4.2/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
|
||||
github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
|
|
@ -173,6 +236,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
|
|||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
|
||||
github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
|
||||
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c h1:wpkoddUomPfHiOziHZixGO5ZBS73cKqVzZipfrLmO1w=
|
||||
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM=
|
||||
github.com/go-shiori/go-readability v0.0.0-20251205110129-5db1dc9836f0 h1:A3B75Yp163FAIf9nLlFMl4pwIj+T3uKxfI7mbvvY2Ls=
|
||||
|
|
@ -213,6 +278,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
|
|||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg=
|
||||
github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||
|
|
@ -247,6 +314,8 @@ github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJ
|
|||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-sqlite3 v1.14.44 h1:3VSe+xafpbzsLbdr2AWlAZk9yRHiBhTBakioXaCKTF8=
|
||||
github.com/mattn/go-sqlite3 v1.14.44/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/mattn/go-sqlite3 v1.14.45 h1:6KA/spDguL3KV8rnybG7ezSaE4SeMR3KC9VbUoAQaIk=
|
||||
github.com/mattn/go-sqlite3 v1.14.45/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -256,6 +325,8 @@ github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd
|
|||
github.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA=
|
||||
github.com/ollama/ollama v0.24.0 h1:CBZ0ffE+cxMWRWau5yD5vXHkiZHAeuxLk+3j55u0XxQ=
|
||||
github.com/ollama/ollama v0.24.0/go.mod h1:lX6J1oDiqbQLNMg9qJbLyEbFwqgM4uD3VDFn6YYy2v4=
|
||||
github.com/ollama/ollama v0.30.7 h1:8RT8xUg0DvVxqU9+naDzqvrqlZo4QQ9Dnvhj6GfEaK0=
|
||||
github.com/ollama/ollama v0.30.7/go.mod h1:TjwyryJftKpcf7ByoIuZWso/Wx2Jr2AcGubxadv13dY=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0=
|
||||
|
|
@ -264,6 +335,8 @@ github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
|||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
|
|
@ -281,6 +354,8 @@ github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
|||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.1 h1:0Gmua0HW1Tv7ANR7hUYwRyD0MG5OJfgvYSZasGZzBic=
|
||||
github.com/quic-go/quic-go v0.59.1/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/quic-go/quic-go v0.60.0 h1:xcQioE8OM66UQLeUMHltK1CCcOu3JbVB4JAQdDQSB+0=
|
||||
github.com/quic-go/quic-go v0.60.0/go.mod h1:wpKpjmPpftl30sL6pFh7REVpjbcCVy4zt2vDyK1TuJk=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
|
|
@ -364,8 +439,12 @@ go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
|||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.5 h1:JVliQq9EGOYaTgMi+k8BhUJyqcGk4ZqeuiN1Cirba9c=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.5/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
golang.org/x/arch v0.27.0 h1:0WNVcR8u9yFz8j5FvdHpgwNp3FS5U4guYdzHwEiGjoU=
|
||||
golang.org/x/arch v0.27.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/arch v0.28.0 h1:wVwVdqsTuUbJvhYVCspQYwZXHNYeLSoZnmHD+ggddpQ=
|
||||
golang.org/x/arch v0.28.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
|
|
@ -375,6 +454,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.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
|
|
@ -384,6 +465,8 @@ golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
|||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
|
@ -408,6 +491,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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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=
|
||||
|
|
@ -426,6 +511,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.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
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=
|
||||
|
|
@ -437,6 +524,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.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
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=
|
||||
|
|
@ -449,6 +537,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.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
@ -464,14 +554,20 @@ gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
|||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.282.0 h1:WmJiSVqUnKqJCpJOx7YADbXaC+9DDsnGSfllFSj7R2I=
|
||||
google.golang.org/api v0.282.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM=
|
||||
google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik=
|
||||
google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM=
|
||||
google.golang.org/genai v1.58.0 h1:MNA3ZkRyr7MnRwZ9RNZ60p4+UMKV3yYRw6pyHq4pp0U=
|
||||
google.golang.org/genai v1.58.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
|
||||
google.golang.org/genai v1.59.0 h1:xp+ydkJFW8hO0hTUaAkr8TrLM9HFP3NYAwFhPd0nDqA=
|
||||
google.golang.org/genai v1.59.0/go.mod h1:mDdPDFXo1Ats7f1WXVyZgWb/CkMzFWTWJruIMy7hGIU=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260608224507-4308a22a1bab h1:cY0oV1VnAqvaim8VsR8ZyEKAudzbRJMRGwD3W/L7yOw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260608224507-4308a22a1bab/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "Bild konnte nicht in %s gespeichert werden: %w",
|
||||
"openai_image_saved_to": "Bild gespeichert unter: %s",
|
||||
"openai_model_no_image_generation": "Modell '%s' unterstützt keine Bildgenerierung. Unterstützte Modelle: %s",
|
||||
"openai_models_rate_limited": "Ratenlimit beim Abrufen der Modelle von Anbieter %s überschritten; erneuter Versuch in %s Sekunden",
|
||||
"openai_models_response_too_large": "Modell-Antwort zu groß von Anbieter %s (>%d Bytes)",
|
||||
"openai_unable_to_parse_models_response": "Modell-Antwort konnte nicht geparst werden; rohe Antwort: %s",
|
||||
"openai_unexpected_status_code_read_error": "unerwarteter Statuscode: %d von Anbieter %s (Fehler beim Lesen der Antwort: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "failed to save image to %s: %w",
|
||||
"openai_image_saved_to": "Image saved to: %s",
|
||||
"openai_model_no_image_generation": "model '%s' does not support image generation. Supported models: %s",
|
||||
"openai_models_rate_limited": "rate limit exceeded fetching models from provider %s; retry after %s seconds",
|
||||
"openai_models_response_too_large": "models response too large from provider %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "unable to parse models response; raw response: %s",
|
||||
"openai_unexpected_status_code_read_error": "unexpected status code: %d from provider %s (failed to read response body: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "no se pudo guardar la imagen en %s: %w",
|
||||
"openai_image_saved_to": "Imagen guardada en: %s",
|
||||
"openai_model_no_image_generation": "el modelo '%s' no soporta generación de imágenes. Modelos soportados: %s",
|
||||
"openai_models_rate_limited": "límite de velocidad excedido al obtener modelos del proveedor %s; reintentar después de %s segundos",
|
||||
"openai_models_response_too_large": "respuesta de modelos demasiado grande del proveedor %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "no se pudo analizar la respuesta de modelos; respuesta cruda: %s",
|
||||
"openai_unexpected_status_code_read_error": "código de estado inesperado: %d del proveedor %s (error al leer cuerpo de respuesta: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "ذخیره تصویر در %s ناموفق بود: %w",
|
||||
"openai_image_saved_to": "تصویر ذخیره شد در: %s",
|
||||
"openai_model_no_image_generation": "مدل '%s' از تولید تصویر پشتیبانی نمیکند. مدلهای پشتیبانی شده: %s",
|
||||
"openai_models_rate_limited": "محدودیت نرخ هنگام دریافت مدلها از ارائهدهنده %s فراتر رفت؛ پس از %s ثانیه دوباره تلاش کنید",
|
||||
"openai_models_response_too_large": "پاسخ مدلها از ارائهدهنده %s بیش از حد بزرگ است (>%d بایت)",
|
||||
"openai_unable_to_parse_models_response": "تجزیه پاسخ مدلها ناموفق بود; پاسخ خام: %s",
|
||||
"openai_unexpected_status_code_read_error": "کد وضعیت غیرمنتظره: %d از ارائهدهنده %s (خطا در خواندن پاسخ: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "échec de l'enregistrement de l'image dans %s : %w",
|
||||
"openai_image_saved_to": "Image enregistrée dans : %s",
|
||||
"openai_model_no_image_generation": "le modèle '%s' ne prend pas en charge la génération d'images. Modèles pris en charge : %s",
|
||||
"openai_models_rate_limited": "limite de débit dépassée lors de la récupération des modèles du fournisseur %s ; réessayer après %s secondes",
|
||||
"openai_models_response_too_large": "réponse des modèles trop volumineuse du fournisseur %s (>%d octets)",
|
||||
"openai_unable_to_parse_models_response": "impossible d'analyser la réponse des modèles ; réponse brute : %s",
|
||||
"openai_unexpected_status_code_read_error": "code d'état inattendu : %d du fournisseur %s (échec de lecture du corps de réponse : %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "salvataggio dell'immagine in %s fallito: %w",
|
||||
"openai_image_saved_to": "Immagine salvata in: %s",
|
||||
"openai_model_no_image_generation": "il modello '%s' non supporta la generazione di immagini. Modelli supportati: %s",
|
||||
"openai_models_rate_limited": "limite di richieste superato durante il recupero dei modelli dal provider %s; riprovare dopo %s secondi",
|
||||
"openai_models_response_too_large": "risposta dei modelli troppo grande dal provider %s (>%d byte)",
|
||||
"openai_unable_to_parse_models_response": "impossibile analizzare risposta modelli; risposta grezza: %s",
|
||||
"openai_unexpected_status_code_read_error": "codice di stato imprevisto: %d dal provider %s (errore lettura corpo risposta: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "画像を %s に保存できませんでした: %w",
|
||||
"openai_image_saved_to": "画像の保存先: %s",
|
||||
"openai_model_no_image_generation": "モデル '%s' は画像生成をサポートしていません。サポートされているモデル: %s",
|
||||
"openai_models_rate_limited": "プロバイダー %s からのモデル取得でレート制限を超過しました。%s 秒後に再試行してください",
|
||||
"openai_models_response_too_large": "プロバイダー %s からのモデルレスポンスが大きすぎます(>%d バイト)",
|
||||
"openai_unable_to_parse_models_response": "モデルレスポンスの解析に失敗しました; 生のレスポンス: %s",
|
||||
"openai_unexpected_status_code_read_error": "予期しないステータスコード: プロバイダー %s から %d (レスポンス本文の読み取りに失敗: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "nie udało się zapisać obrazu do %s: %w",
|
||||
"openai_image_saved_to": "Obraz zapisano do: %s",
|
||||
"openai_model_no_image_generation": "model '%s' nie obsługuje generowania obrazów. Obsługiwane modele: %s",
|
||||
"openai_models_rate_limited": "przekroczono limit żądań podczas pobierania modeli od dostawcy %s; spróbuj ponownie za %s sekund",
|
||||
"openai_models_response_too_large": "odpowiedź z modelami zbyt duża od dostawcy %s (>%d bajtów)",
|
||||
"openai_unable_to_parse_models_response": "nie można przetworzyć odpowiedzi z modelami; surowa odpowiedź: %s",
|
||||
"openai_unexpected_status_code_read_error": "nieoczekiwany kod statusu: %d od dostawcy %s (nie udało się odczytać treści odpowiedzi: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "falha ao salvar a imagem em %s: %w",
|
||||
"openai_image_saved_to": "Imagem salva em: %s",
|
||||
"openai_model_no_image_generation": "o modelo '%s' não suporta geração de imagens. Modelos suportados: %s",
|
||||
"openai_models_rate_limited": "limite de taxa excedido ao buscar modelos do provedor %s; tente novamente após %s segundos",
|
||||
"openai_models_response_too_large": "resposta de modelos muito grande do provedor %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "não foi possível analisar a resposta de modelos; resposta bruta: %s",
|
||||
"openai_unexpected_status_code_read_error": "código de status inesperado: %d do provedor %s (falha ao ler corpo da resposta: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "falha ao guardar a imagem em %s: %w",
|
||||
"openai_image_saved_to": "Imagem guardada em: %s",
|
||||
"openai_model_no_image_generation": "o modelo '%s' não suporta geração de imagens. Modelos suportados: %s",
|
||||
"openai_models_rate_limited": "limite de taxa excedido ao obter modelos do fornecedor %s; tente novamente após %s segundos",
|
||||
"openai_models_response_too_large": "resposta de modelos demasiado grande do fornecedor %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "não foi possível analisar a resposta de modelos; resposta bruta: %s",
|
||||
"openai_unexpected_status_code_read_error": "código de estado inesperado: %d do fornecedor %s (falha ao ler corpo da resposta: %v)",
|
||||
|
|
|
|||
|
|
@ -402,6 +402,7 @@
|
|||
"openai_image_failed_to_save_image": "保存图像到 %s 失败:%w",
|
||||
"openai_image_saved_to": "图像已保存到:%s",
|
||||
"openai_model_no_image_generation": "模型 '%s' 不支持图像生成。支持的模型:%s",
|
||||
"openai_models_rate_limited": "从提供商 %s 获取模型时超出速率限制;请在 %s 秒后重试",
|
||||
"openai_models_response_too_large": "来自提供商 %s 的模型响应过大(>%d 字节)",
|
||||
"openai_unable_to_parse_models_response": "无法解析模型响应;原始响应:%s",
|
||||
"openai_unexpected_status_code_read_error": "意外的状态码:来自提供商 %s 的 %d(读取响应主体失败:%v)",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ const sourcesHeader = "## Sources"
|
|||
func modelDisallowsSamplingParams(model string) bool {
|
||||
// Anthropic's Opus 4.7 and Opus 4.8 models reject non-default sampling parameters.
|
||||
// Omit these params entirely for safest compatibility.
|
||||
return strings.HasPrefix(model, "claude-opus-4-7") || strings.HasPrefix(model, "claude-opus-4-8")
|
||||
return strings.HasPrefix(model, "claude-opus-4-7") || strings.HasPrefix(model, "claude-opus-4-8") || strings.HasPrefix(model, "claude-fable-5")
|
||||
}
|
||||
|
||||
func NewClient() (ret *Client) {
|
||||
|
|
@ -44,6 +44,7 @@ func NewClient() (ret *Client) {
|
|||
ret.defaultRequiredUserMessage = "Hi"
|
||||
ret.models = []string{
|
||||
// The following are the current supported models
|
||||
string(anthropic.ModelClaudeFable5),
|
||||
string(anthropic.ModelClaudeOpus4_8),
|
||||
string(anthropic.ModelClaudeOpus4_7),
|
||||
string(anthropic.ModelClaudeSonnet4_6),
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
|
|
@ -45,6 +46,13 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
return nil, fmt.Errorf(i18n.T("openai_failed_to_create_models_url"), err)
|
||||
}
|
||||
|
||||
// Serve a fresh cached list when available to avoid re-hitting discovery
|
||||
// endpoints that aggressively rate-limit (e.g. GitHub Models' catalog).
|
||||
if models, ok := readModelsCache(providerName, fullURL, modelsCacheTTL); ok {
|
||||
debuglog.Debug(debuglog.Detailed, "Using cached models list for %s (%d models)\n", providerName, len(models))
|
||||
return models, nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -53,6 +61,14 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
// GitHub Models' catalog endpoint sits behind GitHub's edge layer, which
|
||||
// throttles requests that omit the documented API version header (returning
|
||||
// HTTP 429 with an HTML body). Send it so the catalog fetch matches GitHub's
|
||||
// API contract and avoids the edge-level rate limiter.
|
||||
if strings.EqualFold(req.URL.Host, "models.github.ai") {
|
||||
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
|
||||
}
|
||||
|
||||
// Reuse provided HTTP client, or create a new one if not provided
|
||||
client := httpClient
|
||||
if client == nil {
|
||||
|
|
@ -62,17 +78,40 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
// On a network error, fall back to a stale cached list if we have one.
|
||||
if models, ok := readModelsCache(providerName, fullURL, 0); ok {
|
||||
debuglog.Debug(debuglog.Basic, "Fetch failed for %s (%v); serving stale cached models\n", providerName, err)
|
||||
return models, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// A failing discovery call (commonly HTTP 429 rate limiting) should not
|
||||
// hard-fail when we already have a list cached from a prior success.
|
||||
if models, ok := readModelsCache(providerName, fullURL, 0); ok {
|
||||
debuglog.Debug(debuglog.Basic, "Status %d from %s; serving stale cached models\n", resp.StatusCode, providerName)
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// Read the response body for debugging, but limit the number of bytes read
|
||||
bodyBytes, readErr := io.ReadAll(io.LimitReader(resp.Body, errorResponseLimit))
|
||||
if readErr != nil {
|
||||
return nil, fmt.Errorf(i18n.T("openai_unexpected_status_code_read_error"),
|
||||
resp.StatusCode, providerName, readErr)
|
||||
}
|
||||
|
||||
// Rate limiting often returns an HTML body; surface a concise, actionable
|
||||
// message instead of dumping the raw page.
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
retryAfter := strings.TrimSpace(resp.Header.Get("Retry-After"))
|
||||
if retryAfter == "" {
|
||||
retryAfter = "60"
|
||||
}
|
||||
return nil, fmt.Errorf(i18n.T("openai_models_rate_limited"), providerName, retryAfter)
|
||||
}
|
||||
|
||||
bodyString := string(bodyBytes)
|
||||
return nil, fmt.Errorf(i18n.T("openai_unexpected_status_code_with_body"),
|
||||
resp.StatusCode, providerName, bodyString)
|
||||
|
|
@ -97,12 +136,16 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
|
||||
if err := json.Unmarshal(bodyBytes, &openAIFormat); err == nil {
|
||||
debuglog.Debug(debuglog.Detailed, "Successfully parsed models response from %s using OpenAI format (found %d models)\n", providerName, len(openAIFormat.Data))
|
||||
return extractModelIDs(openAIFormat.Data), nil
|
||||
ids := extractModelIDs(openAIFormat.Data)
|
||||
_ = writeModelsCache(providerName, fullURL, ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(bodyBytes, &directArray); err == nil {
|
||||
debuglog.Debug(debuglog.Detailed, "Successfully parsed models response from %s using direct array format (found %d models)\n", providerName, len(directArray))
|
||||
return extractModelIDs(directArray), nil
|
||||
ids := extractModelIDs(directArray)
|
||||
_ = writeModelsCache(providerName, fullURL, ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
var truncatedBody string
|
||||
|
|
|
|||
95
internal/plugins/ai/openai/models_cache.go
Normal file
95
internal/plugins/ai/openai/models_cache.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package openai
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// modelsCacheTTL is how long a successfully fetched model list is considered
|
||||
// fresh. Model catalogs change rarely, so a long TTL avoids re-hitting
|
||||
// discovery endpoints that aggressively rate-limit (e.g. GitHub Models'
|
||||
// catalog, which returns HTTP 429 with a Retry-After header).
|
||||
const modelsCacheTTL = 24 * time.Hour
|
||||
|
||||
// modelsCacheDir returns the directory used to cache provider model lists. It
|
||||
// is a package variable so tests can redirect it to a temporary location.
|
||||
var modelsCacheDir = defaultModelsCacheDir
|
||||
|
||||
func defaultModelsCacheDir() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, ".config", "fabric", "cache", "models"), nil
|
||||
}
|
||||
|
||||
type modelsCacheEntry struct {
|
||||
URL string `json:"url"`
|
||||
FetchedAt time.Time `json:"fetched_at"`
|
||||
Models []string `json:"models"`
|
||||
}
|
||||
|
||||
var cacheNameSanitizer = regexp.MustCompile(`[^A-Za-z0-9_-]+`)
|
||||
|
||||
// modelsCacheFile derives a stable, collision-resistant cache path from the
|
||||
// provider name and the full request URL. The URL is hashed so a provider that
|
||||
// changes its endpoint does not read a stale entry from the old one.
|
||||
func modelsCacheFile(dir, providerName, fullURL string) string {
|
||||
sum := sha256.Sum256([]byte(fullURL))
|
||||
slug := cacheNameSanitizer.ReplaceAllString(providerName, "_")
|
||||
name := fmt.Sprintf("%s-%s.json", slug, hex.EncodeToString(sum[:])[:12])
|
||||
return filepath.Join(dir, name)
|
||||
}
|
||||
|
||||
// readModelsCache returns the cached model list for (providerName, fullURL).
|
||||
// When maxAge > 0 the entry must be younger than maxAge; maxAge <= 0 accepts an
|
||||
// entry of any age (used to fall back to a stale list when a fetch fails).
|
||||
// Empty cached lists are never returned.
|
||||
func readModelsCache(providerName, fullURL string, maxAge time.Duration) ([]string, bool) {
|
||||
dir, err := modelsCacheDir()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
data, err := os.ReadFile(modelsCacheFile(dir, providerName, fullURL))
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
var entry modelsCacheEntry
|
||||
if err := json.Unmarshal(data, &entry); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if entry.URL != fullURL || len(entry.Models) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
if maxAge > 0 && time.Since(entry.FetchedAt) > maxAge {
|
||||
return nil, false
|
||||
}
|
||||
return entry.Models, true
|
||||
}
|
||||
|
||||
// writeModelsCache persists a successfully fetched model list. Empty lists are
|
||||
// not cached so a transient empty response does not stick for the whole TTL.
|
||||
func writeModelsCache(providerName, fullURL string, models []string) error {
|
||||
if len(models) == 0 {
|
||||
return nil
|
||||
}
|
||||
dir, err := modelsCacheDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
entry := modelsCacheEntry{URL: fullURL, FetchedAt: time.Now(), Models: models}
|
||||
data, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(modelsCacheFile(dir, providerName, fullURL), data, 0o600)
|
||||
}
|
||||
165
internal/plugins/ai/openai/models_cache_test.go
Normal file
165
internal/plugins/ai/openai/models_cache_test.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package openai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// withTempModelsCache redirects the models cache to a temporary directory for
|
||||
// the duration of a test and restores the original afterwards.
|
||||
func withTempModelsCache(t *testing.T) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
orig := modelsCacheDir
|
||||
modelsCacheDir = func() (string, error) { return dir, nil }
|
||||
t.Cleanup(func() { modelsCacheDir = orig })
|
||||
return dir
|
||||
}
|
||||
|
||||
func modelsURLFor(t *testing.T, baseURL string) string {
|
||||
t.Helper()
|
||||
full, err := url.JoinPath(baseURL, "models")
|
||||
require.NoError(t, err)
|
||||
return full
|
||||
}
|
||||
|
||||
// writeAgedCache writes a cache entry with an explicit age so freshness and
|
||||
// stale-fallback paths can be exercised deterministically.
|
||||
func writeAgedCache(t *testing.T, dir, provider, fullURL string, models []string, age time.Duration) {
|
||||
t.Helper()
|
||||
entry := modelsCacheEntry{URL: fullURL, FetchedAt: time.Now().Add(-age), Models: models}
|
||||
data, err := json.Marshal(entry)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.MkdirAll(dir, 0o755))
|
||||
require.NoError(t, os.WriteFile(modelsCacheFile(dir, provider, fullURL), data, 0o600))
|
||||
}
|
||||
|
||||
func TestModelsCache_WriteThenReadFresh(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
const provider, fullURL = "GitHub", "https://models.github.ai/catalog/models"
|
||||
|
||||
require.NoError(t, writeModelsCache(provider, fullURL, []string{"a", "b"}))
|
||||
|
||||
models, ok := readModelsCache(provider, fullURL, modelsCacheTTL)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, []string{"a", "b"}, models)
|
||||
}
|
||||
|
||||
func TestModelsCache_EmptyListNotCached(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
const provider, fullURL = "GitHub", "https://models.github.ai/catalog/models"
|
||||
|
||||
require.NoError(t, writeModelsCache(provider, fullURL, nil))
|
||||
|
||||
_, ok := readModelsCache(provider, fullURL, 0)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestModelsCache_ExpiredMissesWithTTLButHitsWithoutAgeLimit(t *testing.T) {
|
||||
dir := withTempModelsCache(t)
|
||||
const provider, fullURL = "GitHub", "https://models.github.ai/catalog/models"
|
||||
writeAgedCache(t, dir, provider, fullURL, []string{"old"}, 48*time.Hour)
|
||||
|
||||
_, ok := readModelsCache(provider, fullURL, modelsCacheTTL)
|
||||
assert.False(t, ok, "entry older than TTL should be a miss")
|
||||
|
||||
models, ok := readModelsCache(provider, fullURL, 0)
|
||||
assert.True(t, ok, "maxAge<=0 should accept any age")
|
||||
assert.Equal(t, []string{"old"}, models)
|
||||
}
|
||||
|
||||
func TestModelsCache_DifferentURLDoesNotCollide(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
require.NoError(t, writeModelsCache("GitHub", "https://a/models", []string{"a"}))
|
||||
|
||||
_, ok := readModelsCache("GitHub", "https://b/models", 0)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
// A fresh cache short-circuits before any network call.
|
||||
func TestFetchModelsDirectly_ServesFreshCacheWithoutRequest(t *testing.T) {
|
||||
dir := withTempModelsCache(t)
|
||||
called := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
writeAgedCache(t, dir, "GitHub", modelsURLFor(t, srv.URL), []string{"cached-model"}, time.Minute)
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"cached-model"}, models)
|
||||
assert.False(t, called, "fresh cache should prevent the network call")
|
||||
}
|
||||
|
||||
// A 429 with a stale cache present returns the stale list rather than erroring.
|
||||
func TestFetchModelsDirectly_429ServesStaleCache(t *testing.T) {
|
||||
dir := withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Retry-After", "60")
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
_, _ = w.Write([]byte("<html>Whoa there!</html>"))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
// Stale so the TTL check misses and the request is actually made.
|
||||
writeAgedCache(t, dir, "GitHub", modelsURLFor(t, srv.URL), []string{"stale-model"}, 48*time.Hour)
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"stale-model"}, models)
|
||||
}
|
||||
|
||||
// A 429 with no cache yields a concise message, not the raw HTML body.
|
||||
func TestFetchModelsDirectly_429NoCacheCleanError(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Retry-After", "60")
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
_, _ = w.Write([]byte("<html><title>Rate limit</title>Whoa there!</html>"))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
_, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "rate limit")
|
||||
assert.Contains(t, err.Error(), "60")
|
||||
assert.NotContains(t, err.Error(), "<html>")
|
||||
assert.NotContains(t, err.Error(), "Whoa there")
|
||||
}
|
||||
|
||||
// A successful fetch is cached, so a later failure is served from cache.
|
||||
func TestFetchModelsDirectly_WritesCacheOnSuccess(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
fail := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if fail {
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`[{"id":"m1"}]`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"m1"}, models)
|
||||
|
||||
// Cache is fresh now, so even with the server failing we get the cached list.
|
||||
fail = true
|
||||
models, err = FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"m1"}, models)
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@ package openai
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -12,6 +14,7 @@ import (
|
|||
// Ensures we can fetch models directly when a provider returns a direct array of models
|
||||
// instead of the standard OpenAI list response structure.
|
||||
func TestFetchModelsDirectly_DirectArray(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/models", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
@ -28,6 +31,7 @@ func TestFetchModelsDirectly_DirectArray(t *testing.T) {
|
|||
|
||||
// Ensures we can fetch models when a provider returns the standard OpenAI format
|
||||
func TestFetchModelsDirectly_OpenAIFormat(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/models", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
@ -42,8 +46,54 @@ func TestFetchModelsDirectly_OpenAIFormat(t *testing.T) {
|
|||
assert.Equal(t, "openai-model", models[0])
|
||||
}
|
||||
|
||||
// Ensures non-GitHub hosts do not receive the GitHub-specific API version header.
|
||||
func TestFetchModelsDirectly_NoGitHubHeaderForOtherHosts(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Empty(t, r.Header.Get("X-GitHub-Api-Version"))
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err := w.Write([]byte(`[{"id":"some-model"}]`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
_, err := FetchModelsDirectly(context.Background(), srv.URL, "test-key", "TestProvider", nil)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// captureRoundTripper records the outgoing request and returns a canned response,
|
||||
// allowing assertions against requests sent to real hosts without networking.
|
||||
type captureRoundTripper struct {
|
||||
req *http.Request
|
||||
body string
|
||||
}
|
||||
|
||||
func (c *captureRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
c.req = req
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||
Body: io.NopCloser(strings.NewReader(c.body)),
|
||||
Request: req,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Ensures the GitHub Models catalog host receives the documented API version
|
||||
// header so it is not throttled by GitHub's edge rate limiter.
|
||||
func TestFetchModelsDirectly_GitHubHostSendsAPIVersionHeader(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
rt := &captureRoundTripper{body: `[{"id":"github-model"}]`}
|
||||
client := &http.Client{Transport: rt}
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), "https://models.github.ai/catalog", "test-key", "GitHub", client)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"github-model"}, models)
|
||||
assert.Equal(t, "2022-11-28", rt.req.Header.Get("X-GitHub-Api-Version"))
|
||||
}
|
||||
|
||||
// Ensures we handle empty model lists correctly
|
||||
func TestFetchModelsDirectly_EmptyArray(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/models", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
|
|||
Loading…
Reference in a new issue