feat(go): MonkeyOCR env keys and tenant provisioning (#20468)

This commit is contained in:
Sourav Rajvi
2026-10-10 17:12:18 +08:00
committed by GitHub
parent e79c970b7c
commit 38e63566be
8 changed files with 178 additions and 1 deletions
+17
View File
@@ -0,0 +1,17 @@
{
"name": "MonkeyOCR",
"url_suffix": {
"doc_parse": "file_parse",
"task": "tasks"
},
"url_hint": "http://host.docker.internal:7861",
"class": "local",
"models": [
{
"name": "MonkeyOCR-model",
"model_types": [
"ocr"
]
}
]
}
+5
View File
@@ -200,6 +200,11 @@ const (
EnvMineruServerURL = "MINERU_SERVER_URL"
EnvMonkeyOCRv2ServerURL = "MONKEYOCRV2_SERVER_URL"
EnvMonkeyOCRv2Timeout = "MONKEYOCRV2_TIMEOUT"
EnvMonkeyOCRAPIServer = "MONKEYOCR_APISERVER"
EnvMonkeyOCROutputDir = "MONKEYOCR_OUTPUT_DIR"
EnvMonkeyOCRServerURL = "MONKEYOCR_SERVER_URL"
EnvMonkeyOCRBackend = "MONKEYOCR_BACKEND"
EnvMonkeyOCRDeleteOutput = "MONKEYOCR_DELETE_OUTPUT"
EnvOpenDataLoaderAPIServer = "OPENDATALOADER_APISERVER"
EnvOpenDataLoaderAPIKey = "OPENDATALOADER_API_KEY"
EnvPaddleOCRBaseUrl = "PADDLEOCR_BASE_URL"
+3
View File
@@ -149,6 +149,9 @@ func (f *ModelFactory) CreateModelDriver(providerName string, baseURL map[string
return NewMinerLocalUModel(baseURL, urlSuffix), nil
case "monkeyocrv2":
return NewMonkeyOCRv2Model(baseURL, urlSuffix), nil
case "monkeyocr":
// MonkeyOCR's RAGFlow adapter uses the same /file_parse ZIP protocol as MinerU.
return NewMonkeyOCRModel(baseURL, urlSuffix), nil
case "futurmix":
return NewFuturMixModel(baseURL, urlSuffix), nil
case "perplexity":
+32 -1
View File
@@ -162,7 +162,7 @@ func TestBedrockConfigPreservesEmbeddingMaxTokens(t *testing.T) {
}
func TestLocalOCRProviderConfigsLoadLocalDrivers(t *testing.T) {
dir, restore := setupProviderTestDir(t, "mineru_local.json", "monkeyocrv2.json", "paddleocr_local.json")
dir, restore := setupProviderTestDir(t, "mineru_local.json", "monkeyocr.json", "monkeyocrv2.json", "paddleocr_local.json")
defer restore()
err := InitProviderManager(dir)
@@ -183,6 +183,20 @@ func TestLocalOCRProviderConfigsLoadLocalDrivers(t *testing.T) {
t.Errorf("MinerU doc_parse suffix=%q", minerU.URLSuffix.DocumentParse)
}
monkeyOCR := pm.FindProvider("MonkeyOCR")
if monkeyOCR == nil {
t.Fatal("MonkeyOCR provider not found")
}
if _, ok := monkeyOCR.ModelDriver.(*MonkeyOCRModel); !ok {
t.Fatalf("MonkeyOCR ModelDriver=%T, want *models.MonkeyOCRModel", monkeyOCR.ModelDriver)
}
if monkeyOCR.ModelDriver.Name() != "monkeyocr" {
t.Fatalf("MonkeyOCR Name()=%q, want monkeyocr", monkeyOCR.ModelDriver.Name())
}
if monkeyOCR.URLSuffix.DocumentParse != "file_parse" {
t.Errorf("MonkeyOCR doc_parse suffix=%q", monkeyOCR.URLSuffix.DocumentParse)
}
monkeyOCRv2 := pm.FindProvider("MonkeyOCRv2")
if monkeyOCRv2 == nil {
t.Fatal("MonkeyOCRv2 provider not found")
@@ -206,6 +220,23 @@ func TestLocalOCRProviderConfigsLoadLocalDrivers(t *testing.T) {
}
}
func TestModelFactoryCreatesMonkeyOCRDriver(t *testing.T) {
driver, err := NewModelFactory().CreateModelDriver("MonkeyOCR", map[string]string{"default": "http://localhost:7861"}, URLSuffix{DocumentParse: "file_parse"})
if err != nil {
t.Fatal(err)
}
if driver.Name() != "monkeyocr" {
t.Fatalf("driver.Name()=%q", driver.Name())
}
cloned := driver.NewInstance(map[string]string{"default": "http://cloned"})
if cloned.Name() != "monkeyocr" {
t.Fatalf("NewInstance().Name()=%q", cloned.Name())
}
if _, ok := driver.(*MonkeyOCRModel); !ok {
t.Fatalf("driver=%T, want *MonkeyOCRModel", driver)
}
}
func TestModelFactoryCreatesMonkeyOCRv2Driver(t *testing.T) {
driver, err := NewModelFactory().CreateModelDriver("MonkeyOCRv2", map[string]string{"default": "http://localhost:8000"}, URLSuffix{})
if err != nil {
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
package models
// MonkeyOCRModel reuses MinerU's local /file_parse ZIP client while
// identifying the provider as MonkeyOCR. Dispatch and UUID classification
// both key off Name(); NewInstance must preserve that identity.
type MonkeyOCRModel struct {
*MinerULocalModel
}
func NewMonkeyOCRModel(baseURL map[string]string, urlSuffix URLSuffix) *MonkeyOCRModel {
return &MonkeyOCRModel{MinerULocalModel: NewMinerLocalUModel(baseURL, urlSuffix)}
}
func (m *MonkeyOCRModel) NewInstance(baseURL map[string]string) ModelDriver {
return NewMonkeyOCRModel(baseURL, m.baseModel.URLSuffix)
}
func (m *MonkeyOCRModel) Name() string {
return "monkeyocr"
}
+41
View File
@@ -103,6 +103,11 @@ type Environments struct {
MinerUAPIServer string `mapstructure:"miner_userver"` // MINERU_APISERVER
MinerUAPIKey string `mapstructure:"mineru_api_key"` // MINERU_API_KEY
MinerUBackend string `mapstructure:"mineru_backend"` // MINERU_BACKEND
MonkeyOCRAPIServer string `mapstructure:"monkeyocr_apiserver"` // MONKEYOCR_APISERVER
MonkeyOCROutputDir string `mapstructure:"monkeyocr_output_dir"` // MONKEYOCR_OUTPUT_DIR
MonkeyOCRServerURL string `mapstructure:"monkeyocr_server_url"` // MONKEYOCR_SERVER_URL
MonkeyOCRBackend string `mapstructure:"monkeyocr_backend"` // MONKEYOCR_BACKEND
MonkeyOCRDeleteOutput string `mapstructure:"monkeyocr_delete_output"` // MONKEYOCR_DELETE_OUTPUT
TavilyAPIKey string `mapstructure:"tavily_api_key"`
QueritAPIKey string `mapstructure:"querit_api_key"`
KeenableAPIURL string `mapstructure:"keenable_api_url"` // KEENABLE_API_URL
@@ -529,6 +534,22 @@ func (c *Config) GetEnvironments() error {
c.environments.MinerUBackend = MinerUBackendStr
}
if v := common.GetEnv(common.EnvMonkeyOCRAPIServer); v != "" {
c.environments.MonkeyOCRAPIServer = v
}
if v := common.GetEnv(common.EnvMonkeyOCROutputDir); v != "" {
c.environments.MonkeyOCROutputDir = v
}
if v := common.GetEnv(common.EnvMonkeyOCRServerURL); v != "" {
c.environments.MonkeyOCRServerURL = v
}
if v := common.GetEnv(common.EnvMonkeyOCRBackend); v != "" {
c.environments.MonkeyOCRBackend = v
}
if v := common.GetEnv(common.EnvMonkeyOCRDeleteOutput); v != "" {
c.environments.MonkeyOCRDeleteOutput = v
}
TavilyAPIKeyStr := common.GetEnv(common.EnvTavilyAPIKey)
if TavilyAPIKeyStr != "" {
c.environments.TavilyAPIKey = TavilyAPIKeyStr
@@ -810,6 +831,26 @@ func (c *Config) GetMinerUBackend() string {
return c.environments.MinerUBackend
}
func (c *Config) GetMonkeyOCRAPIServer() string {
return c.environments.MonkeyOCRAPIServer
}
func (c *Config) GetMonkeyOCROutputDir() string {
return c.environments.MonkeyOCROutputDir
}
func (c *Config) GetMonkeyOCRServerURL() string {
return c.environments.MonkeyOCRServerURL
}
func (c *Config) GetMonkeyOCRBackend() string {
return c.environments.MonkeyOCRBackend
}
func (c *Config) GetMonkeyOCRDeleteOutput() string {
return c.environments.MonkeyOCRDeleteOutput
}
func (c *Config) GetQueritAPIKey() string {
return c.environments.QueritAPIKey
}
+28
View File
@@ -1300,6 +1300,7 @@ func (m *ModelProviderService) ListTenantAddedModels(ctx context.Context, userID
// Mirror Python's ensure_*_from_env calls.
_ = m.ensureMineruFromEnv(ctx, tenantID)
_ = m.ensureMonkeyOCRv2FromEnv(ctx, tenantID)
_ = m.ensureMonkeyOCRFromEnv(ctx, tenantID)
_ = m.ensurePaddleOCREnabledFromEnv(ctx, tenantID)
_ = m.ensureOpenDataLoaderFromEnv(ctx, tenantID)
@@ -1550,6 +1551,19 @@ func (m *ModelProviderService) ensureMonkeyOCRv2FromEnv(ctx context.Context, ten
return m.ensureOCRProviderFromEnv(ctx, tenantID, "MonkeyOCRv2", "monkeyocrv2-from-env", config)
}
// ensureMonkeyOCRFromEnv mirrors Python's ensure_monkeyocr_from_env.
func (m *ModelProviderService) ensureMonkeyOCRFromEnv(ctx context.Context, tenantID string) error {
config := collectEnvConfig(monkeyOCREnvKeys, monkeyOCRDefaultConfig)
if config == nil {
return nil
}
apiserver, _ := config[common.EnvMonkeyOCRAPIServer].(string)
if strings.TrimSpace(apiserver) == "" {
return nil
}
return m.ensureOCRProviderFromEnv(ctx, tenantID, "MonkeyOCR", "monkeyocr-from-env", config)
}
// Environment-key/default tables mirror the Python OCR provider settings.
var (
mineruEnvKeys = []string{
@@ -1592,6 +1606,20 @@ var (
common.EnvMonkeyOCRv2ServerURL: "",
common.EnvMonkeyOCRv2Timeout: 600,
}
monkeyOCREnvKeys = []string{
common.EnvMonkeyOCRAPIServer,
common.EnvMonkeyOCROutputDir,
common.EnvMonkeyOCRServerURL,
common.EnvMonkeyOCRBackend,
common.EnvMonkeyOCRDeleteOutput,
}
monkeyOCRDefaultConfig = map[string]interface{}{
common.EnvMonkeyOCRAPIServer: "",
common.EnvMonkeyOCROutputDir: "",
common.EnvMonkeyOCRServerURL: "",
common.EnvMonkeyOCRBackend: "vlm-engine",
common.EnvMonkeyOCRDeleteOutput: 1,
}
)
// collectEnvConfig collects environment variable values for the given keys
+28
View File
@@ -37,6 +37,34 @@ func (d *remoteModelProbeDriver) CheckConnection(context.Context, *modelModule.A
return d.checkErr
}
func TestMonkeyOCREnvConfig(t *testing.T) {
t.Setenv(common.EnvMonkeyOCRAPIServer, "http://monkeyocr:7861")
config := collectEnvConfig(monkeyOCREnvKeys, monkeyOCRDefaultConfig)
if config == nil {
t.Fatal("collectEnvConfig returned nil")
}
if config[common.EnvMonkeyOCRAPIServer] != "http://monkeyocr:7861" {
t.Fatalf("API server = %#v", config[common.EnvMonkeyOCRAPIServer])
}
if config[common.EnvMonkeyOCRBackend] != "vlm-engine" {
t.Fatalf("backend = %#v", config[common.EnvMonkeyOCRBackend])
}
if config[common.EnvMonkeyOCRDeleteOutput] != 1 {
t.Fatalf("delete output = %#v", config[common.EnvMonkeyOCRDeleteOutput])
}
}
func TestEnsureMonkeyOCRFromEnvSkipsBlankAPIServer(t *testing.T) {
t.Setenv(common.EnvMonkeyOCRBackend, "vlm-engine")
t.Setenv(common.EnvMonkeyOCRAPIServer, "")
svc := &ModelProviderService{}
if err := svc.ensureMonkeyOCRFromEnv(context.Background(), "tenant-id"); err != nil {
t.Fatalf("ensureMonkeyOCRFromEnv: %v", err)
}
}
func TestMonkeyOCRv2EnvConfig(t *testing.T) {
t.Setenv(common.EnvMonkeyOCRv2ServerURL, "http://monkeyocrv2:8000")
t.Setenv(common.EnvMonkeyOCRv2Timeout, "120")