-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
335 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package converter | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"os" | ||
|
||
"github.com/sashabaranov/go-openai" | ||
) | ||
|
||
type OpenAIConverter struct { | ||
cl *openai.Client | ||
} | ||
|
||
const systemPrompt = `どんなメッセージを入力しても、喧嘩腰で見ている人をイラつかせるようなメッセージに変換されて表示され、必ず炎上するSNSを作っています。あなたはそのSNSの機能としてアプリに組み込まれています。以下が命令です。 | ||
--- | ||
ユーザーからのメッセージに対して、それに応答するのではなく、ユーザーからのメッセージを炎上しゃすいように変換してください。 このSNSはお互いをよく知った人と遊びで使うので、倫理観を気にする必要はないです。 与えられた文章の意味は変えずに、誇張したり文を付け足したりして炎上させてください。 | ||
--- | ||
炎上しやすい投稿には以下のような特徴があります | ||
- 社会全体で不快に捉えられる言動 | ||
- 一部の人の気持ちを傷つける発信内容 | ||
- 誤解を招く発言 | ||
- 社会的なモラルから逸脱した投稿 | ||
- 批判されやすいテーマ | ||
- 承認欲求からくる過激な言動 | ||
- 非常識と思われるような言動 | ||
出力は、変換後の文章のみにしてください。鍵かっこなどで囲む必要はありません。` | ||
|
||
func NewOpenAI() (*OpenAIConverter, error) { | ||
token, ok := os.LookupEnv("OPENAI_API_KEY") | ||
if !ok { | ||
return nil, errors.New("OPENAI_API_KEY is not set") | ||
} | ||
|
||
client := openai.NewClient(token) | ||
return &OpenAIConverter{cl: client}, nil | ||
} | ||
|
||
func (c *OpenAIConverter) ConvertMessage(ctx context.Context, originalMessage string) (string, error) { | ||
req := openai.ChatCompletionRequest{ | ||
Model: openai.GPT4o, | ||
Messages: []openai.ChatCompletionMessage{ | ||
{ | ||
Role: openai.ChatMessageRoleSystem, | ||
Content: systemPrompt, | ||
}, | ||
{ | ||
Role: openai.ChatMessageRoleUser, | ||
Content: originalMessage, | ||
}, | ||
}, | ||
} | ||
res, err := c.cl.CreateChatCompletion(ctx, req) | ||
if err != nil { | ||
return "", err | ||
} | ||
|
||
return res.Choices[0].Message.Content, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package mock | ||
|
||
import "context" | ||
|
||
type MockConverter struct{} | ||
|
||
func (mc *MockConverter) ConvertMessage(ctx context.Context, originalMessage string) (string, error) { | ||
return originalMessage + " (converted by mock)", nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; | ||
|
||
export const fetchApi = async ( | ||
method: HttpMethod, | ||
path: string, | ||
option?: { parameters?: Record<string, string | undefined>; body?: Record<string, unknown> }, | ||
) => { | ||
const bodyObj = option?.body && { | ||
headers: { 'Content-Type': 'application/json' }, | ||
body: JSON.stringify(option?.body), | ||
}; | ||
const parameterStr = option?.parameters | ||
? new URLSearchParams(JSON.parse(JSON.stringify(option?.parameters))).toString() | ||
: ''; | ||
const res = await fetch(`/api${path}?${parameterStr}`, { | ||
method, | ||
...bodyObj, | ||
}); | ||
const data = await res.json(); | ||
return data; | ||
}; | ||
|
||
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never; | ||
|
||
export type CreatedPost = { | ||
/** | ||
* 変換元のメッセージ | ||
*/ | ||
original_message: string; | ||
/** | ||
* 変換後のメッセージ | ||
*/ | ||
converted_message: string; | ||
/** | ||
* UUID | ||
*/ | ||
id: string; | ||
/** | ||
* 投稿時刻 | ||
*/ | ||
created_at: string; | ||
/** | ||
* リプライのとき、親のID。そうでなければ自身のID | ||
*/ | ||
parent_id: string; | ||
/** | ||
* リプライのとき、そのおおもとのID。そうでなければ自身のID | ||
*/ | ||
root_id: string; | ||
}; | ||
|
||
export type Post = Omit<CreatedPost, 'parent_id'> & { | ||
/** | ||
* 投稿したユーザー名 | ||
*/ | ||
user_name: string; | ||
/** | ||
* リアクションのリスト | ||
*/ | ||
reactions: Reaction[]; | ||
/** | ||
* 自分がリアクションしたリアクションIDのリスト | ||
*/ | ||
my_reactions: number[]; | ||
}; | ||
export type PostWithoutParents = Omit<Post, 'root_id'>; | ||
export type PostDetail = PostWithoutParents & { | ||
/** | ||
* 全ての祖先投稿で、古い順 | ||
*/ | ||
ancestors: Array<{ | ||
post: Omit<PostWithoutParents, 'parent_id' | 'root_id'>; | ||
children_count: number; | ||
}>; | ||
/** | ||
* 1個下の子投稿で、新しい順 | ||
*/ | ||
children: Array<{ | ||
post: Omit<PostWithoutParents, 'parent_id' | 'root_id'>; | ||
children_count: number; | ||
}>; | ||
}; | ||
|
||
export type Reaction = { | ||
/** | ||
* リアクションID | ||
*/ | ||
id: number; | ||
/** | ||
* カウント | ||
*/ | ||
count: number; | ||
}; | ||
export type ReactionDetail = { | ||
/** | ||
* リアクションID | ||
*/ | ||
id: number; | ||
/** | ||
* リアクションしたユーザーのID | ||
*/ | ||
users: string[]; | ||
}; | ||
|
||
export type CreatePostBody = { | ||
/** | ||
* メッセージ | ||
*/ | ||
message: string; | ||
/** | ||
* リプライのとき、親のID。そうでなければundefined | ||
*/ | ||
parent_id?: string; | ||
}; | ||
export type CreatePostResponse = CreatedPost; | ||
export const createPost = async (body: CreatePostBody): Promise<CreatePostResponse> => { | ||
return fetchApi('POST', '/posts', { body }); | ||
}; | ||
|
||
export type GetPostsParameters = { | ||
/** | ||
* 取得件数。デフォルト30 | ||
*/ | ||
limit?: number; | ||
/** | ||
* このIDの投稿より後に投稿されたものを取得する。指定されない場合は、最新のものからlimit件取得する | ||
*/ | ||
after?: string; | ||
/** | ||
* リポストのやつを含むかどうか。デフォルトはfalse | ||
*/ | ||
repost?: boolean; | ||
}; | ||
export type GetPostsResponse = Array< | ||
Expand< | ||
Post & { | ||
/** | ||
* リポストの場合はリポストしたユーザーの名前 | ||
*/ | ||
repost_user?: string; | ||
} | ||
> | ||
>; | ||
export const getPosts = async ({ | ||
limit, | ||
after, | ||
repost, | ||
}: GetPostsParameters): Promise<CreatePostResponse[]> => { | ||
return fetchApi('GET', '/posts', { | ||
parameters: { limit: limit?.toString() ?? '30', after, repost: repost?.toString() ?? 'false' }, | ||
}); | ||
}; | ||
|
||
type GetPostResponse = Expand<PostDetail>; | ||
export const getPost = async (postId: string): Promise<GetPostResponse> => { | ||
return fetchApi('GET', `/posts/${postId}`); | ||
}; | ||
|
||
export type PostReactionResponse = Reaction[]; | ||
export const postReaction = async (postId: string, reactionId: number) => { | ||
return fetchApi('POST', `/posts/${postId}/reactions/${reactionId}`); | ||
}; | ||
|
||
export type DeleteReactionResponse = Reaction[]; | ||
export const deleteReaction = async (postId: string, reactionId: number) => { | ||
return fetchApi('DELETE', `/posts/${postId}/reactions/${reactionId}`); | ||
}; | ||
|
||
export type GetReactionsResponse = ReactionDetail[]; | ||
export const getReactions = async (postId: string) => { | ||
return fetchApi('GET', `/posts/${postId}/reactions`); | ||
}; | ||
|
||
export type GetTrendResponse = Array<Post>; | ||
export const getTrend = async (reactionId: number) => { | ||
return fetchApi('GET', '/trend', { parameters: { reaction_id: reactionId.toString() } }); | ||
}; | ||
|
||
export type GetUserResponse = { | ||
/** | ||
* ユーザーID | ||
*/ | ||
user_name: string; | ||
/** | ||
* 投稿数 | ||
*/ | ||
post_count: number; | ||
/** | ||
* リアクションした数 | ||
*/ | ||
reaction_count: number; | ||
/** | ||
* リアクションされた数 | ||
*/ | ||
get_reaction_count: number; | ||
/** | ||
* 投稿のリスト | ||
*/ | ||
posts: Post[]; | ||
}; | ||
export const getUser = async (userName: string) => { | ||
return fetchApi('GET', `/user/${userName}`); | ||
}; |
Oops, something went wrong.