feat: add screenshot OCR extraction
This commit is contained in:
@@ -107,6 +107,29 @@ type LLMConfig struct {
|
||||
Providers map[string]LLMProviderConfig `json:"providers"`
|
||||
}
|
||||
|
||||
// OCRProviderConfig stores one cloud OCR endpoint that accepts a screenshot
|
||||
// image and returns extracted text. Aliyun and Volcengine both authenticate
|
||||
// with an AccessKey pair, but their signing schemes use slightly different
|
||||
// endpoint metadata; keeping Region/Service/Action/Version configurable lets
|
||||
// the presets track official API changes without a schema rewrite.
|
||||
type OCRProviderConfig struct {
|
||||
Label string `json:"label"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
AccessKeyID string `json:"accessKeyId"`
|
||||
AccessKeySecret string `json:"accessKeySecret"`
|
||||
Region string `json:"region"`
|
||||
Service string `json:"service"`
|
||||
Action string `json:"action"`
|
||||
Version string `json:"version"`
|
||||
TimeoutSecs int `json:"timeoutSecs"`
|
||||
}
|
||||
|
||||
// OCRConfig controls screenshot text extraction.
|
||||
type OCRConfig struct {
|
||||
ActiveProvider string `json:"activeProvider"`
|
||||
Providers map[string]OCRProviderConfig `json:"providers"`
|
||||
}
|
||||
|
||||
// SSH authentication method identifiers stored in SSHConfig.AuthMethod.
|
||||
const (
|
||||
// SSHAuthBuiltin is the legacy combined method (password → agent → key
|
||||
@@ -129,6 +152,12 @@ const (
|
||||
LLMProviderOpenAI = "openai"
|
||||
)
|
||||
|
||||
// Built-in OCR provider identifiers.
|
||||
const (
|
||||
OCRProviderAliyun = "aliyun"
|
||||
OCRProviderVolcengine = "volcengine"
|
||||
)
|
||||
|
||||
// Theme preference identifiers stored in AppConfig.Theme.
|
||||
const (
|
||||
ThemeAuto = "auto"
|
||||
@@ -172,6 +201,9 @@ type AppConfig struct {
|
||||
|
||||
// LLM holds provider settings for the "copy summary" screenshot action.
|
||||
LLM LLMConfig `json:"llm"`
|
||||
|
||||
// OCR holds provider settings for the "extract text" screenshot action.
|
||||
OCR OCRConfig `json:"ocr"`
|
||||
}
|
||||
|
||||
// DefaultAppConfig returns sane zero-value defaults used on first launch.
|
||||
@@ -190,6 +222,7 @@ func DefaultAppConfig() AppConfig {
|
||||
StrictHostKey: false,
|
||||
},
|
||||
LLM: DefaultLLMConfig(),
|
||||
OCR: DefaultOCRConfig(),
|
||||
}
|
||||
cfg.Normalize()
|
||||
return cfg
|
||||
@@ -234,6 +267,33 @@ func DefaultLLMConfig() LLMConfig {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultOCRConfig returns the built-in OCR provider presets. Users only need
|
||||
// to fill AccessKey credentials for the common regions/actions, while advanced
|
||||
// accounts can still override endpoint metadata from the settings UI.
|
||||
func DefaultOCRConfig() OCRConfig {
|
||||
return OCRConfig{
|
||||
ActiveProvider: OCRProviderAliyun,
|
||||
Providers: map[string]OCRProviderConfig{
|
||||
OCRProviderAliyun: {
|
||||
Label: "阿里云文字识别",
|
||||
Endpoint: "https://ocr-api.cn-hangzhou.aliyuncs.com",
|
||||
Action: "RecognizeGeneral",
|
||||
Version: "2021-07-07",
|
||||
TimeoutSecs: 30,
|
||||
},
|
||||
OCRProviderVolcengine: {
|
||||
Label: "火山引擎文字识别",
|
||||
Endpoint: "https://visual.volcengineapi.com",
|
||||
Region: "cn-north-1",
|
||||
Service: "cv",
|
||||
Action: "OCRNormal",
|
||||
Version: "2020-08-26",
|
||||
TimeoutSecs: 30,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize fills defaults into configs written by older app versions while
|
||||
// preserving any user-provided provider fields.
|
||||
func (c *AppConfig) Normalize() {
|
||||
@@ -295,6 +355,43 @@ func (c *AppConfig) Normalize() {
|
||||
}
|
||||
c.LLM.Providers[id] = current
|
||||
}
|
||||
|
||||
defaultOCR := DefaultOCRConfig()
|
||||
if c.OCR.ActiveProvider == "" {
|
||||
c.OCR.ActiveProvider = defaultOCR.ActiveProvider
|
||||
}
|
||||
if c.OCR.Providers == nil {
|
||||
c.OCR.Providers = map[string]OCRProviderConfig{}
|
||||
}
|
||||
for id, def := range defaultOCR.Providers {
|
||||
current, ok := c.OCR.Providers[id]
|
||||
if !ok {
|
||||
c.OCR.Providers[id] = def
|
||||
continue
|
||||
}
|
||||
if current.Label == "" {
|
||||
current.Label = def.Label
|
||||
}
|
||||
if current.Endpoint == "" {
|
||||
current.Endpoint = def.Endpoint
|
||||
}
|
||||
if current.Region == "" {
|
||||
current.Region = def.Region
|
||||
}
|
||||
if current.Service == "" {
|
||||
current.Service = def.Service
|
||||
}
|
||||
if current.Action == "" {
|
||||
current.Action = def.Action
|
||||
}
|
||||
if current.Version == "" {
|
||||
current.Version = def.Version
|
||||
}
|
||||
if current.TimeoutSecs == 0 {
|
||||
current.TimeoutSecs = def.TimeoutSecs
|
||||
}
|
||||
c.OCR.Providers[id] = current
|
||||
}
|
||||
}
|
||||
|
||||
// IsS3Configured reports whether the user has filled the mandatory S3 fields.
|
||||
@@ -329,3 +426,32 @@ func (c AppConfig) IsLLMConfigured() bool {
|
||||
_, provider, ok := c.ActiveLLMProvider()
|
||||
return ok && provider.BaseURL != "" && provider.APIKey != "" && provider.Model != ""
|
||||
}
|
||||
|
||||
// ActiveOCRProvider returns the selected OCR provider config plus a boolean
|
||||
// indicating whether the selection exists.
|
||||
func (c AppConfig) ActiveOCRProvider() (string, OCRProviderConfig, bool) {
|
||||
id := c.OCR.ActiveProvider
|
||||
if id == "" {
|
||||
id = OCRProviderAliyun
|
||||
}
|
||||
provider, ok := c.OCR.Providers[id]
|
||||
return id, provider, ok
|
||||
}
|
||||
|
||||
// IsOCRConfigured reports whether the selected OCR provider has the fields
|
||||
// required to sign and send a request.
|
||||
func (c AppConfig) IsOCRConfigured() bool {
|
||||
id, provider, ok := c.ActiveOCRProvider()
|
||||
if !ok ||
|
||||
provider.Endpoint == "" ||
|
||||
provider.AccessKeyID == "" ||
|
||||
provider.AccessKeySecret == "" ||
|
||||
provider.Action == "" ||
|
||||
provider.Version == "" {
|
||||
return false
|
||||
}
|
||||
if id == OCRProviderVolcengine && (provider.Region == "" || provider.Service == "") {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user