From e7b2faf9178c543bd4241b4e5eb277ce75d72c9c Mon Sep 17 00:00:00 2001 From: Benjamin Piouffle Date: Sun, 14 Jul 2024 21:02:06 +0200 Subject: [PATCH] Use OpenAI API --- apps/cf/config/config.exs | 6 +- apps/cf/lib/llms/statements_creator.ex | 103 +++++------------- .../statements_extractor_system_prompt.eex | 10 +- apps/cf/mix.exs | 1 + config/releases.exs | 7 +- mix.lock | 1 + 6 files changed, 45 insertions(+), 83 deletions(-) diff --git a/apps/cf/config/config.exs b/apps/cf/config/config.exs index f3af07b1..7ed63549 100644 --- a/apps/cf/config/config.exs +++ b/apps/cf/config/config.exs @@ -44,4 +44,8 @@ config :algoliax, import_config "#{Mix.env()}.exs" config :cf, - openai_api_url: "https://api.perplexity.ai" + openai_model: "gpt-4o" + +config :openai, + beta: "assistants=v2", + http_options: [recv_timeout: 30_000, timeout: 30_000] diff --git a/apps/cf/lib/llms/statements_creator.ex b/apps/cf/lib/llms/statements_creator.ex index 3938c077..f3e9c671 100644 --- a/apps/cf/lib/llms/statements_creator.ex +++ b/apps/cf/lib/llms/statements_creator.ex @@ -9,24 +9,6 @@ defmodule CF.LLMs.StatementsCreator do @max_caption_length 1000 - @model_lama_3_small %{ - name: "llama-3-sonar-small-32k-chat", - parameter_count: "8B", - context_length: 32768 - } - - @model_lama_3_large %{ - name: "llama-3-sonar-large-32k-chat", - parameter_count: "70B", - context_length: 32768 - } - - @model_mistral_7b %{ - name: "mistral-7b-instruct", - parameter_count: "8x7B", - context_length: 16384 - } - # Load prompt messages templates EEx.function_from_file( :defp, @@ -79,77 +61,44 @@ defmodule CF.LLMs.StatementsCreator do """ defp chunk_captions(captions) do # TODO: Base on strings lengths + @max_caption_length + # TODO: Add last captions from previous batch to preserve context Enum.chunk_every(captions, 50) end defp get_llm_suggested_statements(video, captions, retries \\ 0) do - api_key = Application.get_env(:cf, :openai_api_key) - api_url = Application.get_env(:cf, :openai_api_url) - - unless api_key && api_url do - raise "OpenAI API configuration missing" - end - - try do - headers = [ - {"Authorization", "Bearer #{api_key}"}, - {"Content-Type", "application/json"}, - {"Accept", "application/json"} - ] - - system_prompt = generate_system_prompt() - user_prompt = generate_user_prompt(video, captions) - - body = + OpenAI.chat_completion( + model: Application.get_env(:cf, :openai_model), + response_format: %{type: "json_object"}, + stream: false, + messages: [ + %{ + role: "system", + content: generate_system_prompt() + }, %{ - "model" => @model_lama_3_large[:name], - "max_tokens" => - @model_lama_3_large[:context_length] - - String.length(system_prompt) - String.length(user_prompt) - 500, - "stream" => false, - "messages" => [ - %{ - "role" => "system", - "content" => system_prompt - }, - %{ - "role" => "user", - "content" => user_prompt - } - ] + role: "user", + content: generate_user_prompt(video, captions) } - |> Jason.encode!() - - case HTTPoison.post("#{api_url}/chat/completions", body, headers, - timeout: 30_000, - recv_timeout: 30_000 - ) do - {:ok, %HTTPoison.Response{status_code: 200, body: body}} -> - body - |> Jason.decode!() - |> Map.get("choices") - |> List.first() - |> get_in(["message", "content"]) - |> get_json_str_from_content!() - |> Jason.decode!() - |> Map.get("statements") - |> check_statements_input_format!() - - {:ok, %HTTPoison.Response{status_code: status_code, body: body}} -> - raise "Network error: #{status_code} - #{inspect(body)}" - - {:error, %HTTPoison.Error{reason: reason}} -> - raise inspect(reason) - end - rescue - error -> + ] + ) + |> case do + {:ok, %{choices: choices}} -> + choices + |> List.first() + |> get_in(["message", "content"]) + |> get_json_str_from_content!() + |> Jason.decode!() + |> Map.get("statements") + |> check_statements_input_format!() + + {:error, error} -> if retries > 0 do Logger.warn("Failed to get LLM suggested statements: #{inspect(error)}. Retrying...") Process.sleep(1000) get_llm_suggested_statements(video, captions, retries - 1) else Logger.error(inspect(error)) - reraise error, __STACKTRACE__ + raise error end end end diff --git a/apps/cf/lib/llms/templates/statements_extractor_system_prompt.eex b/apps/cf/lib/llms/templates/statements_extractor_system_prompt.eex index 42e3be83..e1d24421 100644 --- a/apps/cf/lib/llms/templates/statements_extractor_system_prompt.eex +++ b/apps/cf/lib/llms/templates/statements_extractor_system_prompt.eex @@ -9,6 +9,7 @@ Renvoie uniquement le résultat en JSON, **sans aucun commentaire ni conclusion* Pour être pertinente, une citation doit : - être vérifiable grâce à l'exposition de faits - faire mention d'une source ou d'un contenu que l'on peut vérifier +- présenter une information unique (découpe les citations qui présentent plusieurs éléments) Et remplir au moins un des critères suivants : - présenter des éléments incomplets ou approximatifs - présenter un argument fallacieux, trompeur ou mensonger @@ -16,6 +17,7 @@ Et remplir au moins un des critères suivants : Ne méritent pas travail de vérification : - les évidences comme "Le ciel est bleu !" +- les annecdotes personelles (ex: "ça a changé ma vie") - les figures de style et l'humour (comme les hyperboles, les métaphores, etc) - les erreurs mineures - les opinions personnelles ("j'aime ça") @@ -30,11 +32,12 @@ Ne méritent pas travail de vérification : "title": "Thinkerview - La diagonale du vide en France" }, "captions": [ - { "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" }, - { "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" }, + { "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" }, + { "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" }, { "start": 89, "text": "reconversion à devenir présentateur météo" }, { "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" }, - { "start": 102, "text": "représentent 42% du territoire national mais alors pourquoi la diagonale du vide comme" } + { "start": 102, "text": "représentent 42% du territoire national mais alors pourquoi la diagonale du vide comme" }, + { "start": 110, "text": "nom? Ça a changé ma vie quand je l'ai découvert" } ] } ``` @@ -45,6 +48,7 @@ Ne méritent pas travail de vérification : { "statements": [ { "time": 10, "text": "Cette mesure sociale [...] n'a fait que créer une augmentation du chômage" }, + { "time": 10, "text": "Cette mesure sociale [...] a provoqué de nombreuses critiques de l'UE" }, { "time": 94, "text": "ici on compte seulement 6,5% de la population française métropolitaine" }, { "time": 94, "text": "ces espaces représentent 42% du territoire national" } ], diff --git a/apps/cf/mix.exs b/apps/cf/mix.exs index 9dc7b54a..e539642e 100644 --- a/apps/cf/mix.exs +++ b/apps/cf/mix.exs @@ -59,6 +59,7 @@ defmodule CF.Mixfile do {:burnex, "~> 3.1"}, {:yaml_elixir, "~> 2.9.0"}, {:jason, "~> 1.4"}, + {:openai, "~> 0.6.1"}, # ---- Internal ---- {:db, in_umbrella: true}, diff --git a/config/releases.exs b/config/releases.exs index b72fa0ca..8c7ed297 100644 --- a/config/releases.exs +++ b/config/releases.exs @@ -87,8 +87,7 @@ config :cf, hard_limitations_period: load_int.({"hard_limitations_period", 3 * 60 * 60}), invitation_system: load_bool.({"invitation_system", "false"}), youtube_api_key: load_secret.({"youtube_api_key", nil}), - openai_api_key: load_secret.("openai_api_key"), - openai_api_url: load_secret.("openai_api_url", "https://api.perplexity.ai"), + openai_model: load_secret.("openai_model"), oauth: [ facebook: [ client_id: load_secret.("facebook_app_id"), @@ -97,6 +96,10 @@ config :cf, ] ] +config :openai, + api_key: load_secret.("openai_api_key"), + organization_key: load_secret("openai_organization_key") + config :cf, CF.Authenticator.GuardianImpl, secret_key: load_secret.("secret_key_base") config :cf, CF.Mailer, diff --git a/mix.lock b/mix.lock index dc02fd81..bbb4b769 100644 --- a/mix.lock +++ b/mix.lock @@ -78,6 +78,7 @@ "nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"}, "not_qwerty123": {:hex, :not_qwerty123, "2.2.1", "656e940159517f2d2f07ea0bb14e4ad376d176b5f4de07115e7a64902b5e13e3", [:mix], [{:gettext, "~> 0.13", [hex: :gettext, repo: "hexpm", optional: false]}], "hexpm", "7637173b09eb7b26b29925039d5b92f7107c94a27cbe4d2ba8efb8b84d060c4b"}, "oauth2": {:hex, :oauth2, "0.9.4", "632e8e8826a45e33ac2ea5ac66dcc019ba6bb5a0d2ba77e342d33e3b7b252c6e", [:mix], [{:hackney, "~> 1.7", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "407c6b9f60aa0d01b915e2347dc6be78adca706a37f0c530808942da3b62e7af"}, + "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"}, "parallel_stream": {:hex, :parallel_stream, "1.0.6", "b967be2b23f0f6787fab7ed681b4c45a215a81481fb62b01a5b750fa8f30f76c", [:mix], [], "hexpm", "639b2e8749e11b87b9eb42f2ad325d161c170b39b288ac8d04c4f31f8f0823eb"}, "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, "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"},