Skip to content

Commit eef9de2

Browse files
authored
Merge pull request #186 from TECH-C-LT/feature/guardians-model-option
Guardian APIにAIのモデルを選択できるオプションを追加
2 parents 7fc9463 + 22c2690 commit eef9de2

File tree

10 files changed

+150
-9
lines changed

10 files changed

+150
-9
lines changed

apps/api/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
"lint": "eslint src --ext .ts,.tsx"
99
},
1010
"dependencies": {
11+
"@ai-sdk/anthropic": "^0.0.43",
12+
"@ai-sdk/google": "^0.0.39",
1113
"@ai-sdk/openai": "^0.0.46",
1214
"@hono/zod-validator": "^0.2.2",
1315
"@peace-net/shared": "workspace:*",

apps/api/src/config/environment.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import { z } from 'zod'
66
const envSchema = z.object({
77
DOCS_URL: z.string(),
88
OPENAI_API_KEY: z.string(),
9+
ANTHROPIC_API_KEY: z.string(),
10+
GOOGLE_API_KEY: z.string(),
911
SUPABASE_URL: z.string(),
1012
SUPABASE_SERVICE_ROLE_KEY: z.string(),
1113
ENCRYPTION_KEY: z.string(),

apps/api/src/features/guardians/guardian.route.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { UsageLogService } from '~/features/usageLogs/usageLog.service'
1515
import { UsageFacade } from '~/features/usages/usage.facade'
1616
import { UserPlanRepository } from '~/features/userPlans/userPlan.repository'
1717
import { UserPlanService } from '~/features/userPlans/userPlan.service'
18+
import { AnthropicClient } from '~/libs/anthropic'
19+
import { GoogleClient } from '~/libs/google'
1820
import { OpenAIClient } from '~/libs/openai'
1921
import { SupabaseClient } from '~/libs/supabase'
2022

@@ -37,7 +39,11 @@ guardianRoutes.post(
3739
async (c) => {
3840
return new GuardianController(
3941
new GuardianUseCase(
40-
new GuardianService(OpenAIClient(getEnv(c).OPENAI_API_KEY)),
42+
new GuardianService(
43+
OpenAIClient(getEnv(c).OPENAI_API_KEY),
44+
AnthropicClient(getEnv(c).ANTHROPIC_API_KEY),
45+
GoogleClient(getEnv(c).GOOGLE_API_KEY),
46+
),
4147
new UsageFacade(
4248
new UserPlanService(
4349
new UserPlanRepository(
@@ -72,7 +78,11 @@ guardianRoutes.post(
7278
async (c) => {
7379
return new GuardianController(
7480
new GuardianUseCase(
75-
new GuardianService(OpenAIClient(getEnv(c).OPENAI_API_KEY)),
81+
new GuardianService(
82+
OpenAIClient(getEnv(c).OPENAI_API_KEY),
83+
AnthropicClient(getEnv(c).ANTHROPIC_API_KEY),
84+
GoogleClient(getEnv(c).GOOGLE_API_KEY),
85+
),
7686
new UsageFacade(
7787
new UserPlanService(
7888
new UserPlanRepository(

apps/api/src/features/guardians/guardian.service.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import { AnthropicProvider } from '@ai-sdk/anthropic'
2+
import { GoogleGenerativeAIProvider } from '@ai-sdk/google'
13
import { OpenAIProvider } from '@ai-sdk/openai'
24
import { categoryScoresSchema } from '@peace-net/shared/schemas/guardian'
3-
import type { CategoryScores } from '@peace-net/shared/types/guardian'
5+
import type { CategoryScores, Models } from '@peace-net/shared/types/guardian'
46
import { generateObject } from 'ai'
57
import { z } from 'zod'
68

@@ -55,7 +57,7 @@ const systemPrompt = `
5557
* @returns 分析結果を含むResultオブジェクト。成功時はGuardianResultを、失敗時はエラーを含みます。
5658
*/
5759
export interface IGuardianService {
58-
guardianText(text: string): Promise<CategoryScores>
60+
guardianText(text: string, selectedModel: Models): Promise<CategoryScores>
5961
}
6062

6163
/**
@@ -71,11 +73,34 @@ export interface IGuardianService {
7173
* @returns 分析結果を含むResultオブジェクト。成功時はGuardianResultを、失敗時はエラーを含みます。
7274
*/
7375
export class GuardianService implements IGuardianService {
74-
constructor(private openai: OpenAIProvider) {}
76+
constructor(
77+
private openai: OpenAIProvider,
78+
private anthropic: AnthropicProvider,
79+
private google: GoogleGenerativeAIProvider,
80+
) {}
81+
82+
async guardianText(
83+
text: string,
84+
selectedModel: Models,
85+
): Promise<CategoryScores> {
86+
let model
87+
switch (selectedModel) {
88+
case 'gpt-4o-mini':
89+
model = this.openai('gpt-4o-mini')
90+
break
91+
case 'claude-3-haiku':
92+
model = this.anthropic('claude-3-haiku-20240307')
93+
break
94+
case 'gemini-1.5-flash':
95+
model = this.google('models/gemini-1.5-flash')
96+
break
97+
default:
98+
model = this.openai('gpt-4o-mini')
99+
break
100+
}
75101

76-
async guardianText(text: string): Promise<CategoryScores> {
77102
const { object } = await generateObject({
78-
model: this.openai('gpt-4o-mini'),
103+
model,
79104
schema: z.object({ category_scores: categoryScoresSchema }),
80105
messages: [
81106
{ role: 'system', content: systemPrompt },

apps/api/src/features/guardians/guardian.usecase.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,18 @@ export class GuardianUseCase implements IGuardianUseCase {
4848
input: GuardianTextInput,
4949
): Promise<Result<GuardianResult>> {
5050
try {
51-
const { text, score_threshold = 0.5, userId, apiKeyId } = input
52-
const categoryScores = await this.guardianService.guardianText(text)
51+
const {
52+
text,
53+
score_threshold = 0.5,
54+
model = 'gpt-4o-mini',
55+
userId,
56+
apiKeyId,
57+
} = input
58+
59+
const categoryScores = await this.guardianService.guardianText(
60+
text,
61+
model,
62+
)
5363

5464
const flagged = checkFlagged(categoryScores, score_threshold)
5565

apps/api/src/libs/anthropic.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createAnthropic } from '@ai-sdk/anthropic'
2+
3+
/**
4+
* Anthropic APIのクライアントを生成します
5+
*
6+
* @param apiKey - Anthropic APIのAPIキー
7+
* @returns Anthropic APIのクライアント
8+
*/
9+
export const AnthropicClient = (apiKey: string) => {
10+
return createAnthropic({
11+
apiKey,
12+
})
13+
}

apps/api/src/libs/google.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { createGoogleGenerativeAI } from '@ai-sdk/google'
2+
3+
/**
4+
* Google Generative AI APIのクライアントを生成します
5+
*
6+
* @param apiKey - Google Generative AI APIのAPIキー
7+
* @returns Google Generative AIのクライアント
8+
*/
9+
export const GoogleClient = (apiKey: string) => {
10+
return createGoogleGenerativeAI({
11+
apiKey,
12+
})
13+
}

packages/shared/src/schemas/guardian.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,16 @@ export const categoryScoresSchema = z.object({
88
defamation: z.number(),
99
})
1010

11+
export const modelsSchema = z.union([
12+
z.literal('gpt-4o-mini'),
13+
z.literal('claude-3-haiku'),
14+
z.literal('gemini-1.5-flash'),
15+
])
16+
1117
export const guardianTextRequestSchema = z.object({
1218
text: z.string().max(500),
1319
score_threshold: z.number().max(1).min(0).optional().default(0.5),
20+
model: modelsSchema.optional().default('gpt-4o-mini'),
1421
})
1522

1623
export const guardianImageRequestSchema = z.object({

packages/shared/src/types/guardian.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import {
44
guardianTextRequestSchema,
55
guardianImageRequestSchema,
66
guardianResultSchema,
7+
modelsSchema,
78
} from '../schemas/guardian'
89

910
export type Category = keyof z.infer<typeof categoryScoresSchema>
1011

1112
export type CategoryScores = z.infer<typeof categoryScoresSchema>
1213

14+
export type Models = z.infer<typeof modelsSchema>
15+
1316
export type GuardianTextDTO = z.infer<typeof guardianTextRequestSchema>
1417

1518
export type GuardianTextInput = GuardianTextDTO & {

pnpm-lock.yaml

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)