Skip to content

Commit 816b5e3

Browse files
committed
Use OpenAI API
1 parent 32f0cac commit 816b5e3

File tree

7 files changed

+47
-87
lines changed

7 files changed

+47
-87
lines changed

apps/cf/config/config.exs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,8 @@ config :algoliax,
4444
import_config "#{Mix.env()}.exs"
4545

4646
config :cf,
47-
openai_api_url: "https://api.perplexity.ai"
47+
openai_model: "gpt-4o"
48+
49+
config :openai,
50+
beta: "assistants=v2",
51+
http_options: [recv_timeout: 30_000, timeout: 30_000]

apps/cf/lib/llms/statements_creator.ex

Lines changed: 28 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,7 @@ defmodule CF.LLMs.StatementsCreator do
77
require EEx
88
require Logger
99

10-
@max_caption_length 1000
11-
12-
@model_lama_3_small %{
13-
name: "llama-3-sonar-small-32k-chat",
14-
parameter_count: "8B",
15-
context_length: 32768
16-
}
17-
18-
@model_lama_3_large %{
19-
name: "llama-3-sonar-large-32k-chat",
20-
parameter_count: "70B",
21-
context_length: 32768
22-
}
23-
24-
@model_mistral_7b %{
25-
name: "mistral-7b-instruct",
26-
parameter_count: "8x7B",
27-
context_length: 16384
28-
}
10+
@captions_chunk_size 300
2911

3012
# Load prompt messages templates
3113
EEx.function_from_file(
@@ -78,78 +60,44 @@ defmodule CF.LLMs.StatementsCreator do
7860
Chunk captions everytime we reach the max caption length
7961
"""
8062
defp chunk_captions(captions) do
81-
# TODO: Base on strings lengths + @max_caption_length
82-
Enum.chunk_every(captions, 50)
63+
# TODO: Add last captions from previous batch to preserve context
64+
Enum.chunk_every(captions, @captions_chunk_size)
8365
end
8466

8567
defp get_llm_suggested_statements(video, captions, retries \\ 0) do
86-
api_key = Application.get_env(:cf, :openai_api_key)
87-
api_url = Application.get_env(:cf, :openai_api_url)
88-
89-
unless api_key && api_url do
90-
raise "OpenAI API configuration missing"
91-
end
92-
93-
try do
94-
headers = [
95-
{"Authorization", "Bearer #{api_key}"},
96-
{"Content-Type", "application/json"},
97-
{"Accept", "application/json"}
98-
]
99-
100-
system_prompt = generate_system_prompt()
101-
user_prompt = generate_user_prompt(video, captions)
102-
103-
body =
68+
OpenAI.chat_completion(
69+
model: Application.get_env(:cf, :openai_model),
70+
response_format: %{type: "json_object"},
71+
stream: false,
72+
messages: [
73+
%{
74+
role: "system",
75+
content: generate_system_prompt()
76+
},
10477
%{
105-
"model" => @model_lama_3_large[:name],
106-
"max_tokens" =>
107-
@model_lama_3_large[:context_length] -
108-
String.length(system_prompt) - String.length(user_prompt) - 500,
109-
"stream" => false,
110-
"messages" => [
111-
%{
112-
"role" => "system",
113-
"content" => system_prompt
114-
},
115-
%{
116-
"role" => "user",
117-
"content" => user_prompt
118-
}
119-
]
78+
role: "user",
79+
content: generate_user_prompt(video, captions)
12080
}
121-
|> Jason.encode!()
122-
123-
case HTTPoison.post("#{api_url}/chat/completions", body, headers,
124-
timeout: 30_000,
125-
recv_timeout: 30_000
126-
) do
127-
{:ok, %HTTPoison.Response{status_code: 200, body: body}} ->
128-
body
129-
|> Jason.decode!()
130-
|> Map.get("choices")
131-
|> List.first()
132-
|> get_in(["message", "content"])
133-
|> get_json_str_from_content!()
134-
|> Jason.decode!()
135-
|> Map.get("statements")
136-
|> check_statements_input_format!()
137-
138-
{:ok, %HTTPoison.Response{status_code: status_code, body: body}} ->
139-
raise "Network error: #{status_code} - #{inspect(body)}"
140-
141-
{:error, %HTTPoison.Error{reason: reason}} ->
142-
raise inspect(reason)
143-
end
144-
rescue
145-
error ->
81+
]
82+
)
83+
|> case do
84+
{:ok, %{choices: choices}} ->
85+
choices
86+
|> List.first()
87+
|> get_in(["message", "content"])
88+
|> get_json_str_from_content!()
89+
|> Jason.decode!()
90+
|> Map.get("statements")
91+
|> check_statements_input_format!()
92+
93+
{:error, error} ->
14694
if retries > 0 do
14795
Logger.warn("Failed to get LLM suggested statements: #{inspect(error)}. Retrying...")
14896
Process.sleep(1000)
14997
get_llm_suggested_statements(video, captions, retries - 1)
15098
else
15199
Logger.error(inspect(error))
152-
reraise error, __STACKTRACE__
100+
raise error
153101
end
154102
end
155103
end

apps/cf/lib/llms/templates/statements_extractor_system_prompt.eex

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ Renvoie uniquement le résultat en JSON, **sans aucun commentaire ni conclusion*
99
Pour être pertinente, une citation doit :
1010
- être vérifiable grâce à l'exposition de faits
1111
- faire mention d'une source ou d'un contenu que l'on peut vérifier
12+
- présenter une information unique (découpe les citations qui présentent plusieurs éléments)
1213
Et remplir au moins un des critères suivants :
1314
- présenter des éléments incomplets ou approximatifs
1415
- présenter un argument fallacieux, trompeur ou mensonger
1516
- présenter des informations intéressantes à vérifier
1617

1718
Ne méritent pas travail de vérification :
1819
- les évidences comme "Le ciel est bleu !"
20+
- les annecdotes personelles (ex: "ça a changé ma vie")
1921
- les figures de style et l'humour (comme les hyperboles, les métaphores, etc)
2022
- les erreurs mineures
2123
- les opinions personnelles ("j'aime ça")
@@ -30,11 +32,12 @@ Ne méritent pas travail de vérification :
3032
"title": "Thinkerview - La diagonale du vide en France"
3133
},
3234
"captions": [
33-
{ "start": 10, "text": "Cette mesure sociale a été un désastre de la pensée ça ne m'évoque que du dégoût elle n'a fait que créer une augmentation du chômage, c'est pour moi une pure folie" },
34-
{ "start": 85, "text": "il y a d'autres zones en France qui sont très peuplées elle s'affiche ici et juste là et oui je sais effectivement je pense que je peux tenter une" },
35+
{ "start": 10, "text": "Cette mesure sociale a été un désastre de la pensée ça ne m'évoque que du dégoût elle n'a fait que créer une augmentation du chômage et a provoqué de nombreuses critiques de l'UE c'était pour moi une pure folie" },
36+
{ "start": 85, "text": "mais parlons de la diagonnale du vite il y a d'autres zones en France qui sont très peuplées elle s'affiche ici et juste là et oui je sais effectivement je pense que je peux tenter une" },
3537
{ "start": 89, "text": "reconversion à devenir présentateur météo" },
3638
{ "start": 94, "text": "dans les zones que vous voyez ici on compte seulement 6,5% de la population française métropolitaine pourtant et bien ces espaces" },
37-
{ "start": 102, "text": "représentent 42% du territoire national mais alors pourquoi la diagonale du vide comme" }
39+
{ "start": 102, "text": "représentent 42% du territoire national mais alors pourquoi la diagonale du vide comme" },
40+
{ "start": 110, "text": "nom? Ça a changé ma vie quand je l'ai découvert" }
3841
]
3942
}
4043
```
@@ -45,6 +48,7 @@ Ne méritent pas travail de vérification :
4548
{
4649
"statements": [
4750
{ "time": 10, "text": "Cette mesure sociale [...] n'a fait que créer une augmentation du chômage" },
51+
{ "time": 10, "text": "Cette mesure sociale [...] a provoqué de nombreuses critiques de l'UE" },
4852
{ "time": 94, "text": "ici on compte seulement 6,5% de la population française métropolitaine" },
4953
{ "time": 94, "text": "ces espaces représentent 42% du territoire national" }
5054
],

apps/cf/mix.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ defmodule CF.Mixfile do
5959
{:burnex, "~> 3.1"},
6060
{:yaml_elixir, "~> 2.9.0"},
6161
{:jason, "~> 1.4"},
62+
{:openai, "~> 0.6.1"},
6263

6364
# ---- Internal ----
6465
{:db, in_umbrella: true},

apps/cf_graphql/lib/schema/schema.ex

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ defmodule CF.Graphql.Schema do
9090
resolve(&Resolvers.Notifications.update_subscription/3)
9191
end
9292

93-
# startAutomaticStatementsExtraction
9493
@desc "Use this to start the automatic statements extraction job. Requires elevated permissions."
9594
field :start_automatic_statements_extraction, :video do
9695
middleware(Middleware.RequireAuthentication)

config/releases.exs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,7 @@ config :cf,
8787
hard_limitations_period: load_int.({"hard_limitations_period", 3 * 60 * 60}),
8888
invitation_system: load_bool.({"invitation_system", "false"}),
8989
youtube_api_key: load_secret.({"youtube_api_key", nil}),
90-
openai_api_key: load_secret.("openai_api_key"),
91-
openai_api_url: load_secret.("openai_api_url", "https://api.perplexity.ai"),
90+
openai_model: load_secret.("openai_model"),
9291
oauth: [
9392
facebook: [
9493
client_id: load_secret.("facebook_app_id"),
@@ -97,6 +96,10 @@ config :cf,
9796
]
9897
]
9998

99+
config :openai,
100+
api_key: load_secret.("openai_api_key"),
101+
organization_key: load_secret("openai_organization_key")
102+
100103
config :cf, CF.Authenticator.GuardianImpl, secret_key: load_secret.("secret_key_base")
101104

102105
config :cf, CF.Mailer,

mix.lock

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
7979
"not_qwerty123": {:hex, :not_qwerty123, "2.2.1", "656e940159517f2d2f07ea0bb14e4ad376d176b5f4de07115e7a64902b5e13e3", [:mix], [{:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: false]}], "hexpm", "7637173b09eb7b26b29925039d5b92f7107c94a27cbe4d2ba8efb8b84d060c4b"},
8080
"oauth2": {:hex, :oauth2, "0.9.4", "632e8e8826a45e33ac2ea5ac66dcc019ba6bb5a0d2ba77e342d33e3b7b252c6e", [:mix], [{:hackney, "~> 1.7", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "407c6b9f60aa0d01b915e2347dc6be78adca706a37f0c530808942da3b62e7af"},
81+
"openai": {:hex, :openai, "0.6.1", "ad86b5b253969fe6d59896d295b1a573cbe44d586fd00bfa8cf3f440d800b4d6", [:mix], [{:httpoison, "~> 2.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "aea82953ea82fcbf91d0474125943becf5d8318af53081ed722a0f26d4346353"},
8182
"parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm", "639b2e8749e11b87b9eb42f2ad325d161c170b39b288ac8d04c4f31f8f0823eb"},
8283
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
8384
"phoenix": {:hex, :phoenix, "1.5.14", "2d5db884be496eefa5157505ec0134e66187cb416c072272420c5509d67bf808", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_html, "~> 2.13 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.0", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.10", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.2", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.1.2 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "207f1aa5520320cbb7940d7ff2dde2342162cf513875848f88249ea0ba02fef7"},

0 commit comments

Comments
 (0)