feat(ai): add Anthropic provider option
This commit is contained in:
parent
d87539a1e1
commit
0ad0fec8d4
12 changed files with 106 additions and 6 deletions
|
|
@ -10,6 +10,8 @@ const (
|
|||
ProviderOpenAICompatible ProviderType = "OPENAI_COMPATIBLE"
|
||||
// ProviderGemini is Google's Gemini API.
|
||||
ProviderGemini ProviderType = "GEMINI"
|
||||
// ProviderAnthropic is Anthropic's API.
|
||||
ProviderAnthropic ProviderType = "ANTHROPIC"
|
||||
)
|
||||
|
||||
// ProviderConfig configures a callable AI provider connection.
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ message InstanceSetting {
|
|||
OPENAI = 1;
|
||||
OPENAI_COMPATIBLE = 2;
|
||||
GEMINI = 3;
|
||||
ANTHROPIC = 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ const (
|
|||
InstanceSetting_OPENAI InstanceSetting_AIProviderType = 1
|
||||
InstanceSetting_OPENAI_COMPATIBLE InstanceSetting_AIProviderType = 2
|
||||
InstanceSetting_GEMINI InstanceSetting_AIProviderType = 3
|
||||
InstanceSetting_ANTHROPIC InstanceSetting_AIProviderType = 4
|
||||
)
|
||||
|
||||
// Enum value maps for InstanceSetting_AIProviderType.
|
||||
|
|
@ -109,12 +110,14 @@ var (
|
|||
1: "OPENAI",
|
||||
2: "OPENAI_COMPATIBLE",
|
||||
3: "GEMINI",
|
||||
4: "ANTHROPIC",
|
||||
}
|
||||
InstanceSetting_AIProviderType_value = map[string]int32{
|
||||
"AI_PROVIDER_TYPE_UNSPECIFIED": 0,
|
||||
"OPENAI": 1,
|
||||
"OPENAI_COMPATIBLE": 2,
|
||||
"GEMINI": 3,
|
||||
"ANTHROPIC": 4,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -1411,7 +1414,7 @@ const file_api_v1_instance_service_proto_rawDesc = "" +
|
|||
"\x04demo\x18\x03 \x01(\bR\x04demo\x12!\n" +
|
||||
"\finstance_url\x18\x06 \x01(\tR\vinstanceUrl\x12(\n" +
|
||||
"\x05admin\x18\a \x01(\v2\x12.memos.api.v1.UserR\x05admin\"\x1b\n" +
|
||||
"\x19GetInstanceProfileRequest\"\xd3\x1a\n" +
|
||||
"\x19GetInstanceProfileRequest\"\xe2\x1a\n" +
|
||||
"\x0fInstanceSetting\x12\x17\n" +
|
||||
"\x04name\x18\x01 \x01(\tB\x03\xe0A\bR\x04name\x12W\n" +
|
||||
"\x0fgeneral_setting\x18\x02 \x01(\v2,.memos.api.v1.InstanceSetting.GeneralSettingH\x00R\x0egeneralSetting\x12W\n" +
|
||||
|
|
@ -1499,14 +1502,15 @@ const file_api_v1_instance_service_proto_rawDesc = "" +
|
|||
"\fMEMO_RELATED\x10\x03\x12\b\n" +
|
||||
"\x04TAGS\x10\x04\x12\x10\n" +
|
||||
"\fNOTIFICATION\x10\x05\x12\x06\n" +
|
||||
"\x02AI\x10\x06\"a\n" +
|
||||
"\x02AI\x10\x06\"p\n" +
|
||||
"\x0eAIProviderType\x12 \n" +
|
||||
"\x1cAI_PROVIDER_TYPE_UNSPECIFIED\x10\x00\x12\n" +
|
||||
"\n" +
|
||||
"\x06OPENAI\x10\x01\x12\x15\n" +
|
||||
"\x11OPENAI_COMPATIBLE\x10\x02\x12\n" +
|
||||
"\n" +
|
||||
"\x06GEMINI\x10\x03:a\xeaA^\n" +
|
||||
"\x06GEMINI\x10\x03\x12\r\n" +
|
||||
"\tANTHROPIC\x10\x04:a\xeaA^\n" +
|
||||
"\x1cmemos.api.v1/InstanceSetting\x12\x1binstance/settings/{setting}*\x10instanceSettings2\x0finstanceSettingB\a\n" +
|
||||
"\x05value\"U\n" +
|
||||
"\x19GetInstanceSettingRequest\x128\n" +
|
||||
|
|
|
|||
|
|
@ -2421,6 +2421,7 @@ components:
|
|||
- OPENAI
|
||||
- OPENAI_COMPATIBLE
|
||||
- GEMINI
|
||||
- ANTHROPIC
|
||||
type: string
|
||||
format: enum
|
||||
endpoint:
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ const (
|
|||
AIProviderType_OPENAI AIProviderType = 1
|
||||
AIProviderType_OPENAI_COMPATIBLE AIProviderType = 2
|
||||
AIProviderType_GEMINI AIProviderType = 3
|
||||
AIProviderType_ANTHROPIC AIProviderType = 4
|
||||
)
|
||||
|
||||
// Enum value maps for AIProviderType.
|
||||
|
|
@ -109,12 +110,14 @@ var (
|
|||
1: "OPENAI",
|
||||
2: "OPENAI_COMPATIBLE",
|
||||
3: "GEMINI",
|
||||
4: "ANTHROPIC",
|
||||
}
|
||||
AIProviderType_value = map[string]int32{
|
||||
"AI_PROVIDER_TYPE_UNSPECIFIED": 0,
|
||||
"OPENAI": 1,
|
||||
"OPENAI_COMPATIBLE": 2,
|
||||
"GEMINI": 3,
|
||||
"ANTHROPIC": 4,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -1321,14 +1324,15 @@ const file_store_instance_setting_proto_rawDesc = "" +
|
|||
"\fMEMO_RELATED\x10\x04\x12\b\n" +
|
||||
"\x04TAGS\x10\x05\x12\x10\n" +
|
||||
"\fNOTIFICATION\x10\x06\x12\x06\n" +
|
||||
"\x02AI\x10\a*a\n" +
|
||||
"\x02AI\x10\a*p\n" +
|
||||
"\x0eAIProviderType\x12 \n" +
|
||||
"\x1cAI_PROVIDER_TYPE_UNSPECIFIED\x10\x00\x12\n" +
|
||||
"\n" +
|
||||
"\x06OPENAI\x10\x01\x12\x15\n" +
|
||||
"\x11OPENAI_COMPATIBLE\x10\x02\x12\n" +
|
||||
"\n" +
|
||||
"\x06GEMINI\x10\x03B\x9f\x01\n" +
|
||||
"\x06GEMINI\x10\x03\x12\r\n" +
|
||||
"\tANTHROPIC\x10\x04B\x9f\x01\n" +
|
||||
"\x0fcom.memos.storeB\x14InstanceSettingProtoP\x01Z)github.com/usememos/memos/proto/gen/store\xa2\x02\x03MSX\xaa\x02\vMemos.Store\xca\x02\vMemos\\Store\xe2\x02\x17Memos\\Store\\GPBMetadata\xea\x02\fMemos::Storeb\x06proto3"
|
||||
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -167,4 +167,5 @@ enum AIProviderType {
|
|||
OPENAI = 1;
|
||||
OPENAI_COMPATIBLE = 2;
|
||||
GEMINI = 3;
|
||||
ANTHROPIC = 4;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,6 +170,8 @@ func convertAIProviderTypeFromStore(providerType storepb.AIProviderType) ai.Prov
|
|||
return ai.ProviderOpenAICompatible
|
||||
case storepb.AIProviderType_GEMINI:
|
||||
return ai.ProviderGemini
|
||||
case storepb.AIProviderType_ANTHROPIC:
|
||||
return ai.ProviderAnthropic
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -523,6 +523,9 @@ func (s *APIV1Service) prepareInstanceAISettingForUpdate(ctx context.Context, se
|
|||
if provider.Type == storepb.AIProviderType_OPENAI && provider.Endpoint == "" {
|
||||
provider.Endpoint = "https://api.openai.com/v1"
|
||||
}
|
||||
if provider.Type == storepb.AIProviderType_ANTHROPIC && provider.Endpoint == "" {
|
||||
provider.Endpoint = "https://api.anthropic.com/v1"
|
||||
}
|
||||
if provider.Type == storepb.AIProviderType_OPENAI_COMPATIBLE && provider.Endpoint == "" {
|
||||
return errors.Errorf("provider %q endpoint is required", provider.Id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,6 +154,47 @@ func TestTranscribe(t *testing.T) {
|
|||
require.Equal(t, "gemini transcript", resp.Text)
|
||||
})
|
||||
|
||||
t.Run("rejects Anthropic transcription as unsupported", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
user, err := ts.CreateRegularUser(ctx, "anthropic-user")
|
||||
require.NoError(t, err)
|
||||
userCtx := ts.CreateUserContext(ctx, user.ID)
|
||||
|
||||
_, err = ts.Store.UpsertInstanceSetting(ctx, &storepb.InstanceSetting{
|
||||
Key: storepb.InstanceSettingKey_AI,
|
||||
Value: &storepb.InstanceSetting_AiSetting{
|
||||
AiSetting: &storepb.InstanceAISetting{
|
||||
Providers: []*storepb.AIProviderConfig{
|
||||
{
|
||||
Id: "anthropic-main",
|
||||
Title: "Anthropic",
|
||||
Type: storepb.AIProviderType_ANTHROPIC,
|
||||
Endpoint: "https://api.anthropic.com/v1",
|
||||
ApiKey: "sk-ant-test",
|
||||
Models: []string{"claude-sonnet-4-5"},
|
||||
DefaultModel: "claude-sonnet-4-5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = ts.Service.Transcribe(userCtx, &v1pb.TranscribeRequest{
|
||||
ProviderId: "anthropic-main",
|
||||
Config: &v1pb.TranscriptionConfig{},
|
||||
Audio: &v1pb.TranscriptionAudio{
|
||||
Source: &v1pb.TranscriptionAudio_Content{Content: []byte("RIFF")},
|
||||
Filename: "voice.wav",
|
||||
ContentType: "audio/wav",
|
||||
},
|
||||
})
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "capability unsupported")
|
||||
})
|
||||
|
||||
t.Run("rejects unconfigured model", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
|
|
|||
|
|
@ -624,4 +624,39 @@ func TestUpdateInstanceSetting(t *testing.T) {
|
|||
require.Equal(t, []string{"gpt-5.4-mini", "gpt-5.4"}, stored.GetProviders()[0].GetModels())
|
||||
require.Equal(t, "gpt-5.4-mini", stored.GetProviders()[0].GetDefaultModel())
|
||||
})
|
||||
|
||||
t.Run("UpdateInstanceSetting - Anthropic provider gets default endpoint", func(t *testing.T) {
|
||||
ts := NewTestService(t)
|
||||
defer ts.Cleanup()
|
||||
|
||||
hostUser, err := ts.CreateHostUser(ctx, "admin")
|
||||
require.NoError(t, err)
|
||||
adminCtx := ts.CreateUserContext(ctx, hostUser.ID)
|
||||
|
||||
_, err = ts.Service.UpdateInstanceSetting(adminCtx, &v1pb.UpdateInstanceSettingRequest{
|
||||
Setting: &v1pb.InstanceSetting{
|
||||
Name: "instance/settings/AI",
|
||||
Value: &v1pb.InstanceSetting_AiSetting{
|
||||
AiSetting: &v1pb.InstanceSetting_AISetting{
|
||||
Providers: []*v1pb.InstanceSetting_AIProviderConfig{
|
||||
{
|
||||
Id: "anthropic-main",
|
||||
Title: "Anthropic",
|
||||
Type: v1pb.InstanceSetting_ANTHROPIC,
|
||||
ApiKey: "sk-ant-test",
|
||||
Models: []string{"claude-sonnet-4-5"},
|
||||
DefaultModel: "claude-sonnet-4-5",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
stored, err := ts.Store.GetInstanceAISetting(ctx)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, stored.GetProviders(), 1)
|
||||
require.Equal(t, "https://api.anthropic.com/v1", stored.GetProviders()[0].GetEndpoint())
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ const providerTypeOptions = [
|
|||
InstanceSetting_AIProviderType.OPENAI,
|
||||
InstanceSetting_AIProviderType.OPENAI_COMPATIBLE,
|
||||
InstanceSetting_AIProviderType.GEMINI,
|
||||
InstanceSetting_AIProviderType.ANTHROPIC,
|
||||
];
|
||||
|
||||
const createProviderID = () => {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue