raif 1.2.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -5
  3. data/app/assets/builds/raif.css +4 -1
  4. data/app/assets/builds/raif_admin.css +13 -1
  5. data/app/assets/javascript/raif/controllers/conversations_controller.js +1 -1
  6. data/app/assets/stylesheets/raif/admin/conversation.scss +16 -0
  7. data/app/assets/stylesheets/raif/conversations.scss +3 -0
  8. data/app/assets/stylesheets/raif.scss +2 -1
  9. data/app/controllers/raif/admin/application_controller.rb +16 -0
  10. data/app/controllers/raif/admin/configs_controller.rb +94 -0
  11. data/app/controllers/raif/admin/model_completions_controller.rb +18 -1
  12. data/app/controllers/raif/admin/model_tool_invocations_controller.rb +7 -1
  13. data/app/controllers/raif/admin/stats/model_tool_invocations_controller.rb +21 -0
  14. data/app/controllers/raif/admin/stats/tasks_controller.rb +15 -6
  15. data/app/controllers/raif/admin/stats_controller.rb +32 -3
  16. data/app/controllers/raif/conversation_entries_controller.rb +1 -0
  17. data/app/controllers/raif/conversations_controller.rb +10 -2
  18. data/app/jobs/raif/conversation_entry_job.rb +8 -6
  19. data/app/models/raif/admin/task_stat.rb +7 -0
  20. data/app/models/raif/agent.rb +63 -2
  21. data/app/models/raif/agents/native_tool_calling_agent.rb +101 -56
  22. data/app/models/raif/application_record.rb +18 -0
  23. data/app/models/raif/concerns/agent_inference_stats.rb +35 -0
  24. data/app/models/raif/concerns/has_llm.rb +1 -1
  25. data/app/models/raif/concerns/json_schema_definition.rb +40 -5
  26. data/app/models/raif/concerns/llms/anthropic/message_formatting.rb +28 -0
  27. data/app/models/raif/concerns/llms/anthropic/response_tool_calls.rb +24 -0
  28. data/app/models/raif/concerns/llms/anthropic/tool_formatting.rb +4 -0
  29. data/app/models/raif/concerns/llms/bedrock/message_formatting.rb +36 -0
  30. data/app/models/raif/concerns/llms/bedrock/response_tool_calls.rb +26 -0
  31. data/app/models/raif/concerns/llms/bedrock/tool_formatting.rb +4 -0
  32. data/app/models/raif/concerns/llms/google/message_formatting.rb +109 -0
  33. data/app/models/raif/concerns/llms/google/response_tool_calls.rb +32 -0
  34. data/app/models/raif/concerns/llms/google/tool_formatting.rb +72 -0
  35. data/app/models/raif/concerns/llms/message_formatting.rb +11 -5
  36. data/app/models/raif/concerns/llms/open_ai/json_schema_validation.rb +3 -3
  37. data/app/models/raif/concerns/llms/open_ai_completions/message_formatting.rb +22 -0
  38. data/app/models/raif/concerns/llms/open_ai_completions/response_tool_calls.rb +22 -0
  39. data/app/models/raif/concerns/llms/open_ai_completions/tool_formatting.rb +4 -0
  40. data/app/models/raif/concerns/llms/open_ai_responses/message_formatting.rb +17 -0
  41. data/app/models/raif/concerns/llms/open_ai_responses/response_tool_calls.rb +26 -0
  42. data/app/models/raif/concerns/llms/open_ai_responses/tool_formatting.rb +4 -0
  43. data/app/models/raif/concerns/run_with.rb +127 -0
  44. data/app/models/raif/conversation.rb +96 -9
  45. data/app/models/raif/conversation_entry.rb +37 -8
  46. data/app/models/raif/embedding_model.rb +2 -1
  47. data/app/models/raif/embedding_models/open_ai.rb +1 -1
  48. data/app/models/raif/llm.rb +28 -3
  49. data/app/models/raif/llms/anthropic.rb +7 -19
  50. data/app/models/raif/llms/bedrock.rb +6 -20
  51. data/app/models/raif/llms/google.rb +140 -0
  52. data/app/models/raif/llms/open_ai_base.rb +19 -5
  53. data/app/models/raif/llms/open_ai_completions.rb +6 -11
  54. data/app/models/raif/llms/open_ai_responses.rb +6 -16
  55. data/app/models/raif/llms/open_router.rb +10 -14
  56. data/app/models/raif/model_completion.rb +61 -0
  57. data/app/models/raif/model_tool.rb +10 -2
  58. data/app/models/raif/model_tool_invocation.rb +38 -6
  59. data/app/models/raif/model_tools/agent_final_answer.rb +2 -7
  60. data/app/models/raif/model_tools/provider_managed/code_execution.rb +4 -0
  61. data/app/models/raif/model_tools/provider_managed/image_generation.rb +4 -0
  62. data/app/models/raif/model_tools/provider_managed/web_search.rb +4 -0
  63. data/app/models/raif/streaming_responses/google.rb +71 -0
  64. data/app/models/raif/task.rb +74 -18
  65. data/app/models/raif/user_tool_invocation.rb +19 -0
  66. data/app/views/layouts/raif/admin.html.erb +12 -1
  67. data/app/views/raif/admin/agents/_agent.html.erb +8 -0
  68. data/app/views/raif/admin/agents/_conversation_message.html.erb +28 -6
  69. data/app/views/raif/admin/agents/index.html.erb +2 -0
  70. data/app/views/raif/admin/agents/show.html.erb +46 -1
  71. data/app/views/raif/admin/configs/show.html.erb +117 -0
  72. data/app/views/raif/admin/conversations/_conversation_entry.html.erb +29 -34
  73. data/app/views/raif/admin/conversations/show.html.erb +2 -0
  74. data/app/views/raif/admin/model_completions/_model_completion.html.erb +9 -0
  75. data/app/views/raif/admin/model_completions/index.html.erb +26 -0
  76. data/app/views/raif/admin/model_completions/show.html.erb +124 -61
  77. data/app/views/raif/admin/model_tool_invocations/index.html.erb +22 -1
  78. data/app/views/raif/admin/model_tools/_list.html.erb +16 -0
  79. data/app/views/raif/admin/model_tools/_model_tool.html.erb +36 -0
  80. data/app/views/raif/admin/stats/_stats_tile.html.erb +34 -0
  81. data/app/views/raif/admin/stats/index.html.erb +71 -88
  82. data/app/views/raif/admin/stats/model_tool_invocations/index.html.erb +43 -0
  83. data/app/views/raif/admin/stats/tasks/index.html.erb +20 -6
  84. data/app/views/raif/admin/tasks/index.html.erb +6 -1
  85. data/app/views/raif/admin/tasks/show.html.erb +36 -3
  86. data/app/views/raif/conversation_entries/_form.html.erb +4 -1
  87. data/app/views/raif/conversations/_conversation.html.erb +10 -0
  88. data/app/views/raif/conversations/_entry_processed.turbo_stream.erb +12 -0
  89. data/app/views/raif/conversations/_full_conversation.html.erb +3 -6
  90. data/app/views/raif/conversations/_initial_chat_message.html.erb +5 -0
  91. data/app/views/raif/conversations/index.html.erb +23 -0
  92. data/config/locales/admin.en.yml +33 -1
  93. data/config/locales/en.yml +41 -4
  94. data/config/routes.rb +2 -0
  95. data/db/migrate/20250804013843_add_task_run_args_to_raif_tasks.rb +13 -0
  96. data/db/migrate/20250811171150_make_raif_task_creator_optional.rb +8 -0
  97. data/db/migrate/20250904194456_add_generating_entry_response_to_raif_conversations.rb +7 -0
  98. data/db/migrate/20250911125234_add_source_to_raif_tasks.rb +7 -0
  99. data/db/migrate/20251020005853_add_source_to_raif_agents.rb +7 -0
  100. data/db/migrate/20251020011346_rename_task_run_args_to_run_with.rb +7 -0
  101. data/db/migrate/20251020011405_add_run_with_to_raif_agents.rb +13 -0
  102. data/db/migrate/20251024160119_add_llm_messages_max_length_to_raif_conversations.rb +14 -0
  103. data/db/migrate/20251124185033_add_provider_tool_call_id_to_raif_model_tool_invocations.rb +7 -0
  104. data/db/migrate/20251128202941_add_tool_choice_to_raif_model_completions.rb +7 -0
  105. data/db/migrate/20260118144846_add_source_to_raif_conversations.rb +7 -0
  106. data/db/migrate/20260119000000_add_failure_tracking_to_raif_model_completions.rb +10 -0
  107. data/db/migrate/20260119000001_add_completed_at_to_raif_model_completions.rb +8 -0
  108. data/db/migrate/20260119000002_add_started_at_to_raif_model_completions.rb +8 -0
  109. data/exe/raif +7 -0
  110. data/lib/generators/raif/agent/agent_generator.rb +22 -7
  111. data/lib/generators/raif/agent/templates/agent.rb.tt +20 -24
  112. data/lib/generators/raif/agent/templates/agent_eval_set.rb.tt +48 -0
  113. data/lib/generators/raif/agent/templates/application_agent.rb.tt +1 -3
  114. data/lib/generators/raif/base_generator.rb +19 -0
  115. data/lib/generators/raif/conversation/conversation_generator.rb +21 -2
  116. data/lib/generators/raif/conversation/templates/application_conversation.rb.tt +0 -2
  117. data/lib/generators/raif/conversation/templates/conversation.rb.tt +34 -32
  118. data/lib/generators/raif/conversation/templates/conversation_eval_set.rb.tt +70 -0
  119. data/lib/generators/raif/eval_set/eval_set_generator.rb +28 -0
  120. data/lib/generators/raif/eval_set/templates/eval_set.rb.tt +21 -0
  121. data/lib/generators/raif/evals/setup/setup_generator.rb +47 -0
  122. data/lib/generators/raif/install/install_generator.rb +15 -0
  123. data/lib/generators/raif/install/templates/initializer.rb +89 -10
  124. data/lib/generators/raif/model_tool/model_tool_generator.rb +5 -5
  125. data/lib/generators/raif/model_tool/templates/model_tool.rb.tt +78 -78
  126. data/lib/generators/raif/model_tool/templates/model_tool_invocation_partial.html.erb.tt +1 -1
  127. data/lib/generators/raif/task/task_generator.rb +22 -3
  128. data/lib/generators/raif/task/templates/application_task.rb.tt +0 -2
  129. data/lib/generators/raif/task/templates/task.rb.tt +55 -59
  130. data/lib/generators/raif/task/templates/task_eval_set.rb.tt +54 -0
  131. data/lib/raif/cli/base.rb +39 -0
  132. data/lib/raif/cli/evals.rb +47 -0
  133. data/lib/raif/cli/evals_setup.rb +27 -0
  134. data/lib/raif/cli.rb +67 -0
  135. data/lib/raif/configuration.rb +57 -8
  136. data/lib/raif/engine.rb +8 -0
  137. data/lib/raif/errors/instance_dependent_schema_error.rb +8 -0
  138. data/lib/raif/errors/streaming_error.rb +6 -3
  139. data/lib/raif/errors.rb +1 -0
  140. data/lib/raif/evals/eval.rb +30 -0
  141. data/lib/raif/evals/eval_set.rb +111 -0
  142. data/lib/raif/evals/eval_sets/expectations.rb +53 -0
  143. data/lib/raif/evals/eval_sets/llm_judge_expectations.rb +255 -0
  144. data/lib/raif/evals/expectation_result.rb +39 -0
  145. data/lib/raif/evals/llm_judge.rb +32 -0
  146. data/lib/raif/evals/llm_judges/binary.rb +94 -0
  147. data/lib/raif/evals/llm_judges/comparative.rb +89 -0
  148. data/lib/raif/evals/llm_judges/scored.rb +63 -0
  149. data/lib/raif/evals/llm_judges/summarization.rb +166 -0
  150. data/lib/raif/evals/run.rb +202 -0
  151. data/lib/raif/evals/scoring_rubric.rb +174 -0
  152. data/lib/raif/evals.rb +26 -0
  153. data/lib/raif/json_schema_builder.rb +14 -0
  154. data/lib/raif/llm_registry.rb +218 -15
  155. data/lib/raif/messages.rb +180 -0
  156. data/lib/raif/migration_checker.rb +3 -3
  157. data/lib/raif/utils/colors.rb +23 -0
  158. data/lib/raif/utils.rb +1 -0
  159. data/lib/raif/version.rb +1 -1
  160. data/lib/raif.rb +13 -0
  161. data/lib/tasks/annotate_rb.rake +10 -0
  162. data/spec/support/current_temperature_test_tool.rb +34 -0
  163. data/spec/support/rspec_helpers.rb +8 -8
  164. data/spec/support/test_conversation.rb +1 -1
  165. metadata +77 -10
  166. data/app/models/raif/agents/re_act_agent.rb +0 -127
  167. data/app/models/raif/agents/re_act_step.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e3f7403ecd4de813aef00da87918f1e7335df85f2e89593ac61f89fb3f6eb6b
4
- data.tar.gz: 2f8247a1a4d249157bc85afa14df34127631a8821adf790ed88db9c4c58b38e6
3
+ metadata.gz: 3187cd7316780e6263633670b3fadb2deff54c2ca92e626c4251158dbf3b1c57
4
+ data.tar.gz: ec6ec786edc5eef76ff84dfcb0a59b73343be60d3be4afd8e87f6dacb3bf2703
5
5
  SHA512:
6
- metadata.gz: d7c573743a9aa6011994de4ddbf705cbed47f4c4d0fc269428315777a2c22e87fabf4f146cfaac5810a58dc4ab8a7efdfd8b5bb4ec69d1d617a2d5a5937c355d
7
- data.tar.gz: d1a0f35875fd4dacf565f44b330a066e47fad000aa53d70804785a60cd2e3ace6c3c05d051094dfb3b951f2550be349246f68e299287cc2bb5ea07b700f38034
6
+ metadata.gz: d12a91c035140e6bf11bebc6ca20f1979c199f1e2426e060bbc3f3e15dc45effccb6a698c7c3e08db480884a5e39d035453d5f38829114f1c8b964a4470e4e4a
7
+ data.tar.gz: eb599630424965e5f679512640c70a908f37e66f05c1e5035b941b3cb8d6d7fb04b183031020419169f1ee37c950d4f0c16d946e097ac0f32ac3995c17b9b8d8
data/README.md CHANGED
@@ -17,6 +17,7 @@ Raif is built by [Cultivate Labs](https://www.cultivatelabs.com) and is used to
17
17
  - [Conversations](https://docs.raif.ai/key_raif_concepts/conversations)
18
18
  - [Agents](https://docs.raif.ai/key_raif_concepts/agents)
19
19
  - [Model Tools](https://docs.raif.ai/key_raif_concepts/model_tools)
20
+ - [Evals](https://docs.raif.ai/key_raif_concepts/evals)
20
21
  - [Images/Files/PDF's](https://docs.raif.ai/learn_more/images_files_pdfs)
21
22
  - [Embedding Models](https://docs.raif.ai/learn_more/embedding_models)
22
23
  - [Web Admin](https://docs.raif.ai/learn_more/web_admin)
@@ -36,11 +37,11 @@ View the [chatting with the LLM docs](https://docs.raif.ai/getting_started/chatt
36
37
 
37
38
  # Key Raif Concepts
38
39
 
39
- [Tasks](https://docs.raif.ai/key_raif_concepts/tasks)
40
- [Conversations](https://docs.raif.ai/key_raif_concepts/conversations)
41
- [Agents](https://docs.raif.ai/key_raif_concepts/agents)
42
- [Model Tools](https://docs.raif.ai/key_raif_concepts/model_tools)
43
-
40
+ - [Tasks](https://docs.raif.ai/key_raif_concepts/tasks)
41
+ - [Conversations](https://docs.raif.ai/key_raif_concepts/conversations)
42
+ - [Agents](https://docs.raif.ai/key_raif_concepts/agents)
43
+ - [Model Tools](https://docs.raif.ai/key_raif_concepts/model_tools)
44
+ - [Evals](https://docs.raif.ai/key_raif_concepts/evals)
44
45
 
45
46
  # Images/Files/PDF's
46
47
 
@@ -95,5 +95,8 @@
95
95
  box-shadow: 0.3em -0.3em 0 0 currentcolor;
96
96
  }
97
97
  }
98
+ .raif-conversation-entries-container {
99
+ scroll-behavior: smooth;
100
+ }
98
101
 
99
- /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInJhaWYuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0VBQ0UseUJBQXlCO0VBQ3pCLG1CQUFtQjtFQUNuQixrQkFBa0I7RUFDbEIsV0FBVztFQUNYLFlBQVk7RUFDWixjQUFjO0VBQ2QscUJBQXFCO0FBQ3ZCOztBQUVBOztFQUVFLFdBQVc7RUFDWCxjQUFjO0VBQ2Qsa0JBQWtCO0VBQ2xCLE1BQU07RUFDTixPQUFPO0VBQ1AsY0FBYztFQUNkLGVBQWU7RUFDZixrQkFBa0I7RUFDbEIseUJBQXlCO0VBQ3pCLGtDQUFrQztBQUNwQzs7QUFFQTtFQUNFLGNBQWM7RUFDZCx5QkFBeUI7RUFDekIscUJBQXFCO0FBQ3ZCOztBQUVBO0VBQ0UscUJBQXFCO0VBQ3JCLFVBQVU7RUFDVixhQUFhO0VBQ2IsbUJBQW1CO0VBQ25CLDhCQUE4QjtFQUM5Qiw0QkFBNEI7RUFDNUIsZUFBZTtFQUNmLGdCQUFnQjtFQUNoQixrQkFBa0I7QUFDcEI7O0FBRUE7O0VBRUUsYUFBYTtBQUNmOztBQUVBO0VBQ0U7SUFDRSxVQUFVO0VBQ1o7RUFDQTtJQUNFLFVBQVU7RUFDWjtBQUNGO0FBQ0E7RUFDRTtJQUNFLDhDQUE4QztFQUNoRDtFQUNBO0lBQ0UsZ0RBQWdEO0VBQ2xEO0FBQ0Y7QUFDQTtFQUNFO0lBQ0UsNkNBQTZDO0VBQy9DO0VBQ0E7SUFDRSxnREFBZ0Q7RUFDbEQ7QUFDRjtBQUNBO0VBQ0U7SUFDRSx3Q0FBd0M7RUFDMUM7RUFDQTtJQUNFLHdDQUF3QztFQUMxQztFQUNBO0lBQ0Usc0NBQXNDO0VBQ3hDO0VBQ0E7SUFDRSx5Q0FBeUM7RUFDM0M7RUFDQTtJQUNFLHFDQUFxQztFQUN2QztFQUNBO0lBQ0UsMENBQTBDO0VBQzVDO0VBQ0E7SUFDRSx1Q0FBdUM7RUFDekM7RUFDQTtJQUNFLHlDQUF5QztFQUMzQztBQUNGIiwiZmlsZSI6InJhaWYuY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLnJhaWYtbG9hZGVyIHtcbiAgdHJhbnNmb3JtOiByb3RhdGVaKDQ1ZGVnKTtcbiAgcGVyc3BlY3RpdmU6IDEwMDBweDtcbiAgYm9yZGVyLXJhZGl1czogNTAlO1xuICB3aWR0aDogMjVweDtcbiAgaGVpZ2h0OiAyNXB4O1xuICBjb2xvcjogIzM4NzRmZjtcbiAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xufVxuXG4ucmFpZi1sb2FkZXI6YmVmb3JlLFxuLnJhaWYtbG9hZGVyOmFmdGVyIHtcbiAgY29udGVudDogXCJcIjtcbiAgZGlzcGxheTogYmxvY2s7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiAwO1xuICBsZWZ0OiAwO1xuICB3aWR0aDogaW5oZXJpdDtcbiAgaGVpZ2h0OiBpbmhlcml0O1xuICBib3JkZXItcmFkaXVzOiA1MCU7XG4gIHRyYW5zZm9ybTogcm90YXRlWCg3MGRlZyk7XG4gIGFuaW1hdGlvbjogMXMgc3BpbiBsaW5lYXIgaW5maW5pdGU7XG59XG5cbi5yYWlmLWxvYWRlcjphZnRlciB7XG4gIGNvbG9yOiAjMjViMDAzO1xuICB0cmFuc2Zvcm06IHJvdGF0ZVkoNzBkZWcpO1xuICBhbmltYXRpb24tZGVsYXk6IDAuNHM7XG59XG5cbi5yYWlmLXN0cmVhbWluZy1jdXJzb3Ige1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG4gIHdpZHRoOiAycHg7XG4gIGhlaWdodDogMS4xZW07XG4gIG1hcmdpbi1ib3R0b206IC0ycHg7XG4gIGJhY2tncm91bmQtY29sb3I6IGN1cnJlbnRDb2xvcjtcbiAgYW5pbWF0aW9uOiBibGluayAxcyBpbmZpbml0ZTtcbiAgdHJhbnNmb3JtOiBub25lO1xuICBib3JkZXItcmFkaXVzOiAwO1xuICBwb3NpdGlvbjogcmVsYXRpdmU7XG59XG5cbi5yYWlmLXN0cmVhbWluZy1jdXJzb3I6YmVmb3JlLFxuLnJhaWYtc3RyZWFtaW5nLWN1cnNvcjphZnRlciB7XG4gIGRpc3BsYXk6IG5vbmU7XG59XG5cbkBrZXlmcmFtZXMgYmxpbmsge1xuICAwJSwgNTAlIHtcbiAgICBvcGFjaXR5OiAxO1xuICB9XG4gIDUxJSwgMTAwJSB7XG4gICAgb3BhY2l0eTogMDtcbiAgfVxufVxuQGtleWZyYW1lcyByb3RhdGUge1xuICAwJSB7XG4gICAgdHJhbnNmb3JtOiB0cmFuc2xhdGUoLTUwJSwgLTUwJSkgcm90YXRlWigwZGVnKTtcbiAgfVxuICAxMDAlIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZSgtNTAlLCAtNTAlKSByb3RhdGVaKDM2MGRlZyk7XG4gIH1cbn1cbkBrZXlmcmFtZXMgcm90YXRlY2N3IHtcbiAgMCUge1xuICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKC01MCUsIC01MCUpIHJvdGF0ZSgwZGVnKTtcbiAgfVxuICAxMDAlIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZSgtNTAlLCAtNTAlKSByb3RhdGUoLTM2MGRlZyk7XG4gIH1cbn1cbkBrZXlmcmFtZXMgc3BpbiB7XG4gIDAlLCAxMDAlIHtcbiAgICBib3gtc2hhZG93OiAwLjNlbSAwcHggMCAwcHggY3VycmVudGNvbG9yO1xuICB9XG4gIDEyJSB7XG4gICAgYm94LXNoYWRvdzogMC4zZW0gMC4zZW0gMCAwIGN1cnJlbnRjb2xvcjtcbiAgfVxuICAyNSUge1xuICAgIGJveC1zaGFkb3c6IDAgMC4zZW0gMCAwcHggY3VycmVudGNvbG9yO1xuICB9XG4gIDM3JSB7XG4gICAgYm94LXNoYWRvdzogLTAuM2VtIDAuM2VtIDAgMCBjdXJyZW50Y29sb3I7XG4gIH1cbiAgNTAlIHtcbiAgICBib3gtc2hhZG93OiAtMC4zZW0gMCAwIDAgY3VycmVudGNvbG9yO1xuICB9XG4gIDYyJSB7XG4gICAgYm94LXNoYWRvdzogLTAuM2VtIC0wLjNlbSAwIDAgY3VycmVudGNvbG9yO1xuICB9XG4gIDc1JSB7XG4gICAgYm94LXNoYWRvdzogMHB4IC0wLjNlbSAwIDAgY3VycmVudGNvbG9yO1xuICB9XG4gIDg3JSB7XG4gICAgYm94LXNoYWRvdzogMC4zZW0gLTAuM2VtIDAgMCBjdXJyZW50Y29sb3I7XG4gIH1cbn1cbiJdfQ== */
102
+ /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInJhaWYuY3NzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBO0VBQ0UseUJBQXlCO0VBQ3pCLG1CQUFtQjtFQUNuQixrQkFBa0I7RUFDbEIsV0FBVztFQUNYLFlBQVk7RUFDWixjQUFjO0VBQ2QscUJBQXFCO0FBQ3ZCOztBQUVBOztFQUVFLFdBQVc7RUFDWCxjQUFjO0VBQ2Qsa0JBQWtCO0VBQ2xCLE1BQU07RUFDTixPQUFPO0VBQ1AsY0FBYztFQUNkLGVBQWU7RUFDZixrQkFBa0I7RUFDbEIseUJBQXlCO0VBQ3pCLGtDQUFrQztBQUNwQzs7QUFFQTtFQUNFLGNBQWM7RUFDZCx5QkFBeUI7RUFDekIscUJBQXFCO0FBQ3ZCOztBQUVBO0VBQ0UscUJBQXFCO0VBQ3JCLFVBQVU7RUFDVixhQUFhO0VBQ2IsbUJBQW1CO0VBQ25CLDhCQUE4QjtFQUM5Qiw0QkFBNEI7RUFDNUIsZUFBZTtFQUNmLGdCQUFnQjtFQUNoQixrQkFBa0I7QUFDcEI7O0FBRUE7O0VBRUUsYUFBYTtBQUNmOztBQUVBO0VBQ0U7SUFDRSxVQUFVO0VBQ1o7RUFDQTtJQUNFLFVBQVU7RUFDWjtBQUNGO0FBQ0E7RUFDRTtJQUNFLDhDQUE4QztFQUNoRDtFQUNBO0lBQ0UsZ0RBQWdEO0VBQ2xEO0FBQ0Y7QUFDQTtFQUNFO0lBQ0UsNkNBQTZDO0VBQy9DO0VBQ0E7SUFDRSxnREFBZ0Q7RUFDbEQ7QUFDRjtBQUNBO0VBQ0U7SUFDRSx3Q0FBd0M7RUFDMUM7RUFDQTtJQUNFLHdDQUF3QztFQUMxQztFQUNBO0lBQ0Usc0NBQXNDO0VBQ3hDO0VBQ0E7SUFDRSx5Q0FBeUM7RUFDM0M7RUFDQTtJQUNFLHFDQUFxQztFQUN2QztFQUNBO0lBQ0UsMENBQTBDO0VBQzVDO0VBQ0E7SUFDRSx1Q0FBdUM7RUFDekM7RUFDQTtJQUNFLHlDQUF5QztFQUMzQztBQUNGO0FBQ0E7RUFDRSx1QkFBdUI7QUFDekIiLCJmaWxlIjoicmFpZi5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyIucmFpZi1sb2FkZXIge1xuICB0cmFuc2Zvcm06IHJvdGF0ZVooNDVkZWcpO1xuICBwZXJzcGVjdGl2ZTogMTAwMHB4O1xuICBib3JkZXItcmFkaXVzOiA1MCU7XG4gIHdpZHRoOiAyNXB4O1xuICBoZWlnaHQ6IDI1cHg7XG4gIGNvbG9yOiAjMzg3NGZmO1xuICBkaXNwbGF5OiBpbmxpbmUtYmxvY2s7XG59XG5cbi5yYWlmLWxvYWRlcjpiZWZvcmUsXG4ucmFpZi1sb2FkZXI6YWZ0ZXIge1xuICBjb250ZW50OiBcIlwiO1xuICBkaXNwbGF5OiBibG9jaztcbiAgcG9zaXRpb246IGFic29sdXRlO1xuICB0b3A6IDA7XG4gIGxlZnQ6IDA7XG4gIHdpZHRoOiBpbmhlcml0O1xuICBoZWlnaHQ6IGluaGVyaXQ7XG4gIGJvcmRlci1yYWRpdXM6IDUwJTtcbiAgdHJhbnNmb3JtOiByb3RhdGVYKDcwZGVnKTtcbiAgYW5pbWF0aW9uOiAxcyBzcGluIGxpbmVhciBpbmZpbml0ZTtcbn1cblxuLnJhaWYtbG9hZGVyOmFmdGVyIHtcbiAgY29sb3I6ICMyNWIwMDM7XG4gIHRyYW5zZm9ybTogcm90YXRlWSg3MGRlZyk7XG4gIGFuaW1hdGlvbi1kZWxheTogMC40cztcbn1cblxuLnJhaWYtc3RyZWFtaW5nLWN1cnNvciB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgd2lkdGg6IDJweDtcbiAgaGVpZ2h0OiAxLjFlbTtcbiAgbWFyZ2luLWJvdHRvbTogLTJweDtcbiAgYmFja2dyb3VuZC1jb2xvcjogY3VycmVudENvbG9yO1xuICBhbmltYXRpb246IGJsaW5rIDFzIGluZmluaXRlO1xuICB0cmFuc2Zvcm06IG5vbmU7XG4gIGJvcmRlci1yYWRpdXM6IDA7XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbn1cblxuLnJhaWYtc3RyZWFtaW5nLWN1cnNvcjpiZWZvcmUsXG4ucmFpZi1zdHJlYW1pbmctY3Vyc29yOmFmdGVyIHtcbiAgZGlzcGxheTogbm9uZTtcbn1cblxuQGtleWZyYW1lcyBibGluayB7XG4gIDAlLCA1MCUge1xuICAgIG9wYWNpdHk6IDE7XG4gIH1cbiAgNTElLCAxMDAlIHtcbiAgICBvcGFjaXR5OiAwO1xuICB9XG59XG5Aa2V5ZnJhbWVzIHJvdGF0ZSB7XG4gIDAlIHtcbiAgICB0cmFuc2Zvcm06IHRyYW5zbGF0ZSgtNTAlLCAtNTAlKSByb3RhdGVaKDBkZWcpO1xuICB9XG4gIDEwMCUge1xuICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKC01MCUsIC01MCUpIHJvdGF0ZVooMzYwZGVnKTtcbiAgfVxufVxuQGtleWZyYW1lcyByb3RhdGVjY3cge1xuICAwJSB7XG4gICAgdHJhbnNmb3JtOiB0cmFuc2xhdGUoLTUwJSwgLTUwJSkgcm90YXRlKDBkZWcpO1xuICB9XG4gIDEwMCUge1xuICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKC01MCUsIC01MCUpIHJvdGF0ZSgtMzYwZGVnKTtcbiAgfVxufVxuQGtleWZyYW1lcyBzcGluIHtcbiAgMCUsIDEwMCUge1xuICAgIGJveC1zaGFkb3c6IDAuM2VtIDBweCAwIDBweCBjdXJyZW50Y29sb3I7XG4gIH1cbiAgMTIlIHtcbiAgICBib3gtc2hhZG93OiAwLjNlbSAwLjNlbSAwIDAgY3VycmVudGNvbG9yO1xuICB9XG4gIDI1JSB7XG4gICAgYm94LXNoYWRvdzogMCAwLjNlbSAwIDBweCBjdXJyZW50Y29sb3I7XG4gIH1cbiAgMzclIHtcbiAgICBib3gtc2hhZG93OiAtMC4zZW0gMC4zZW0gMCAwIGN1cnJlbnRjb2xvcjtcbiAgfVxuICA1MCUge1xuICAgIGJveC1zaGFkb3c6IC0wLjNlbSAwIDAgMCBjdXJyZW50Y29sb3I7XG4gIH1cbiAgNjIlIHtcbiAgICBib3gtc2hhZG93OiAtMC4zZW0gLTAuM2VtIDAgMCBjdXJyZW50Y29sb3I7XG4gIH1cbiAgNzUlIHtcbiAgICBib3gtc2hhZG93OiAwcHggLTAuM2VtIDAgMCBjdXJyZW50Y29sb3I7XG4gIH1cbiAgODclIHtcbiAgICBib3gtc2hhZG93OiAwLjNlbSAtMC4zZW0gMCAwIGN1cnJlbnRjb2xvcjtcbiAgfVxufVxuLnJhaWYtY29udmVyc2F0aW9uLWVudHJpZXMtY29udGFpbmVyIHtcbiAgc2Nyb2xsLWJlaGF2aW9yOiBzbW9vdGg7XG59XG4iXX0= */
@@ -20,6 +20,18 @@
20
20
  .conversation-history .message.assistant .message-content {
21
21
  background-color: rgb(232, 255, 244);
22
22
  }
23
+ .conversation-history .message.tool_call {
24
+ border-left: 3px solid #f59e0b;
25
+ }
26
+ .conversation-history .message.tool_call .message-content {
27
+ background-color: #fffbeb;
28
+ }
29
+ .conversation-history .message.tool_call_result {
30
+ border-left: 3px solid rgb(25, 135, 84);
31
+ }
32
+ .conversation-history .message.tool_call_result .message-content {
33
+ background-color: rgb(232, 255, 244);
34
+ }
23
35
  .conversation-history .message .message-content {
24
36
  border-radius: 4px;
25
37
  overflow: hidden;
@@ -267,4 +279,4 @@ body.raif-admin {
267
279
  }
268
280
  }
269
281
 
270
- /*# sourceMappingURL=data:application/json;base64,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QUNBO0VBQ0UseUJBQXlCO0VBQ3pCLGNBQWM7RUFDZCxxQkFBcUI7QUFDdkI7QUFDQTtFQUNFLHlCQUF5QjtFQUN6QixrQkFBa0I7RUFDbEIsYUFBYTtFQUNiLGdCQUFnQjtFQUNoQixtQkFBbUI7RUFDbkIsY0FBYztFQUNkLHlCQUF5QjtBQUMzQjtBQUNBO0VBQ0UscUJBQXFCO0FBQ3ZCO0FBQ0E7RUFDRSxxQkFBcUI7RUFDckIsYUFBYTtBQUNmO0FBQ0E7RUFDRSwyQkFBMkI7RUFDM0IsNEJBQTRCO0FBQzlCO0FBQ0E7RUFDRSw4QkFBOEI7RUFDOUIsK0JBQStCO0FBQ2pDO0FBQ0E7RUFDRSxjQUFjO0VBQ2QscUJBQXFCO0VBQ3JCLHVCQUF1QjtBQUN6QjtBQUNBO0VBQ0UseUJBQXlCO0VBQ3pCLHFCQUFxQjtFQUNyQixjQUFjO0FBQ2hCO0FBQ0E7RUFDRSx5QkFBeUI7RUFDekIscUJBQXFCO0FBQ3ZCO0FBQ0E7RUFDRSxjQUFjO0FBQ2hCO0FBQ0E7RUFDRSxZQUFZO0VBQ1osa0JBQWtCO0VBQ2xCLDJFQUEyRTtBQUM3RTtBQUNBO0VBQ0UseUNBQXlDO0VBQ3pDLGNBQWM7QUFDaEI7QUFDQTtFQUNFLHlCQUF5QjtBQUMzQjtBQUNBO0VBQ0U7SUFDRSxnQkFBZ0I7SUFDaEIsWUFBWTtJQUNaLGNBQWM7RUFDaEI7RUFDQTtJQUNFLFNBQVM7SUFDVCxnQkFBZ0I7RUFDbEI7RUFDQTtJQUNFLGlCQUFpQjtFQUNuQjtBQUNGIiwiZmlsZSI6InJhaWZfYWRtaW4uY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLmNvbnZlcnNhdGlvbi1jYXJkIC5jYXJkLWJvZHkge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhmOWZhO1xufVxuXG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2Uge1xuICBwb3NpdGlvbjogcmVsYXRpdmU7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2U6bGFzdC1jaGlsZCB7XG4gIG1hcmdpbi1ib3R0b206IDA7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UudXNlciB7XG4gIGJvcmRlci1sZWZ0OiAzcHggc29saWQgIzNiODJmNjtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZS51c2VyIC5tZXNzYWdlLWNvbnRlbnQge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjBmOWZmO1xufVxuLmNvbnZlcnNhdGlvbi1oaXN0b3J5IC5tZXNzYWdlLmFzc2lzdGFudCB7XG4gIGJvcmRlci1sZWZ0OiAzcHggc29saWQgcmdiKDI1LCAxMzUsIDg0KTtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZS5hc3Npc3RhbnQgLm1lc3NhZ2UtY29udGVudCB7XG4gIGJhY2tncm91bmQtY29sb3I6IHJnYigyMzIsIDI1NSwgMjQ0KTtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZSAubWVzc2FnZS1jb250ZW50IHtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBvdmVyZmxvdzogaGlkZGVuO1xuICBib3gtc2hhZG93OiAwIDFweCAycHggcmdiYSgwLCAwLCAwLCAwLjA1KTtcbiAgZm9udC1zaXplOiAwLjg3NXJlbTtcbiAgbGluZS1oZWlnaHQ6IDEuNTtcbiAgY29sb3I6ICMxZTNhOGE7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UgLm1lc3NhZ2UtY29udGVudCBjb2RlIHtcbiAgZGlzcGxheTogYmxvY2s7XG4gIGZvbnQtZmFtaWx5OiBTRk1vbm8tUmVndWxhciwgTWVubG8sIE1vbmFjbywgQ29uc29sYXMsIFwiTGliZXJhdGlvbiBNb25vXCIsIFwiQ291cmllciBOZXdcIiwgbW9ub3NwYWNlO1xuICB3aGl0ZS1zcGFjZTogcHJlLXdyYXA7XG4gIHdvcmQtYnJlYWs6IGJyZWFrLXdvcmQ7XG4gIGJhY2tncm91bmQ6IG5vbmU7XG4gIGJvcmRlcjogbm9uZTtcbiAgcGFkZGluZzogMDtcbiAgY29sb3I6IGluaGVyaXQ7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UgLm1lc3NhZ2UtY29udGVudDpsYXN0LWNoaWxkIHtcbiAgbWFyZ2luLWJvdHRvbTogMDtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZSAubWVzc2FnZS1jb250ZW50IC5tZXNzYWdlLWxhYmVsIHtcbiAgZGlzcGxheTogaW5saW5lLWJsb2NrO1xuICBmb250LXNpemU6IDAuNzVyZW07XG4gIGZvbnQtd2VpZ2h0OiA2MDA7XG4gIHRleHQtdHJhbnNmb3JtOiB1cHBlcmNhc2U7XG4gIGxldHRlci1zcGFjaW5nOiAwLjVweDtcbn1cblxuYm9keS5yYWlmLWFkbWluIHtcbiAgZm9udC1mYW1pbHk6IC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgXCJTZWdvZSBVSVwiLCBSb2JvdG8sIEhlbHZldGljYSwgQXJpYWwsIHNhbnMtc2VyaWY7XG4gIGNvbG9yOiAjNTI1ZjdmO1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhmYWZjO1xuICBsaW5lLWhlaWdodDogMS41O1xuICBmb250LXNpemU6IDAuOTM3NXJlbTtcbn1cblxuLnJhaWYtYWRtaW4gLm5hdmJhci1icmFuZC1sb2dvIHtcbiAgd2lkdGg6IDEwMHB4O1xufVxuLnJhaWYtYWRtaW4gaDEsXG4ucmFpZi1hZG1pbiBoMixcbi5yYWlmLWFkbWluIGgzLFxuLnJhaWYtYWRtaW4gaDQsXG4ucmFpZi1hZG1pbiBoNSxcbi5yYWlmLWFkbWluIGg2IHtcbiAgY29sb3I6ICMxZTNhOGE7XG4gIGZvbnQtd2VpZ2h0OiA2MDA7XG59XG4ucmFpZi1hZG1pbiBhIHtcbiAgY29sb3I6ICMzYjgyZjY7XG4gIHRleHQtZGVjb3JhdGlvbjogbm9uZTtcbiAgdHJhbnNpdGlvbjogYWxsIDAuMnMgZWFzZTtcbn1cbi5yYWlmLWFkbWluIGE6aG92ZXIge1xuICBjb2xvcjogcmdiKDExLjE1MTIxOTUxMjIsIDk5LjEyMTk1MTIxOTUsIDI0Mi44NDg3ODA0ODc4KTtcbn1cbi5yYWlmLWFkbWluIC5uYXZiYXIge1xuICBib3gtc2hhZG93OiAwIDJweCA1cHggMCByZ2JhKDUwLCA1MCwgOTMsIDAuMSk7XG4gIHBhZGRpbmc6IDAuNzVyZW0gMS41cmVtO1xufVxuLnJhaWYtYWRtaW4gLm5hdmJhci5uYXZiYXItZGFyayB7XG4gIGJhY2tncm91bmQ6IGxpbmVhci1ncmFkaWVudCgxMzVkZWcsICMxZTNhOGEsIHJnYigyMC44OTI4NTcxNDI5LCA0MC4zOTI4NTcxNDI5LCA5Ni4xMDcxNDI4NTcxKSkgIWltcG9ydGFudDtcbn1cbi5yYWlmLWFkbWluIC5uYXZiYXIgLm5hdmJhci1icmFuZCB7XG4gIGZvbnQtd2VpZ2h0OiA3MDA7XG4gIGxldHRlci1zcGFjaW5nOiAwLjVweDtcbn1cbi5yYWlmLWFkbWluIC5zaWRlYmFyIHtcbiAgYm94LXNoYWRvdzogaW5zZXQgLTFweCAwIDAgI2UyZThmMDtcbiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGUgIWltcG9ydGFudDtcbiAgcGFkZGluZy10b3A6IDFyZW07XG4gIGhlaWdodDogMTAwdmg7XG59XG4ucmFpZi1hZG1pbiAuc2lkZWJhciAubmF2LWxpbmsge1xuICBjb2xvcjogIzUyNWY3ZjtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBtYXJnaW46IDAuMjVyZW0gMC43NXJlbTtcbiAgcGFkZGluZzogMC41cmVtIDAuNzVyZW07XG59XG4ucmFpZi1hZG1pbiAuc2lkZWJhciAubmF2LWxpbms6aG92ZXIge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjBmOWZmO1xufVxuLnJhaWYtYWRtaW4gLnNpZGViYXIgLm5hdi1saW5rLmFjdGl2ZSB7XG4gIGJhY2tncm91bmQtY29sb3I6IHJnYmEoNTksIDEzMCwgMjQ2LCAwLjEpO1xuICBjb2xvcjogIzNiODJmNjtcbiAgZm9udC13ZWlnaHQ6IDUwMDtcbn1cbi5yYWlmLWFkbWluIG1haW4ge1xuICBwYWRkaW5nLXRvcDogMS41cmVtO1xuICBwYWRkaW5nLWJvdHRvbTogM3JlbTtcbn1cbi5yYWlmLWFkbWluIC5jYXJkIHtcbiAgYm9yZGVyOiBub25lO1xuICBib3JkZXItcmFkaXVzOiA0cHg7XG4gIGJveC1zaGFkb3c6IDAgNHB4IDZweCByZ2JhKDUwLCA1MCwgOTMsIDAuMTEpLCAwIDFweCAzcHggcmdiYSgwLCAwLCAwLCAwLjA4KTtcbiAgbWFyZ2luLWJvdHRvbTogMS41cmVtO1xuICBvdmVyZmxvdzogaGlkZGVuO1xufVxuLnJhaWYtYWRtaW4gLmNhcmQgLmNhcmQtaGVhZGVyIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGU7XG4gIGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZTJlOGYwO1xuICBwYWRkaW5nOiAxcmVtIDEuMjVyZW07XG59XG4ucmFpZi1hZG1pbiAuY2FyZCAuY2FyZC1oZWFkZXIgaDUge1xuICBtYXJnaW4tYm90dG9tOiAwO1xuICBmb250LXNpemU6IDFyZW07XG4gIGZvbnQtd2VpZ2h0OiA2MDA7XG59XG4ucmFpZi1hZG1pbiAuY2FyZCAuY2FyZC1ib2R5IHtcbiAgcGFkZGluZzogMS4yNXJlbTtcbn1cbi5yYWlmLWFkbWluIC5jYXJkLmNvbnZlcnNhdGlvbi1jYXJkIC5jYXJkLWJvZHkge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjhmOWZhO1xufVxuLnJhaWYtYWRtaW4gLnRhYmxlIHtcbiAgbWFyZ2luLWJvdHRvbTogMDtcbn1cbi5yYWlmLWFkbWluIC50YWJsZSB0aCB7XG4gIGZvbnQtd2VpZ2h0OiA2MDA7XG4gIGNvbG9yOiAjMWUzYThhO1xuICBib3JkZXItdG9wOiBub25lO1xuICBwYWRkaW5nOiAxcmVtIDAuNzVyZW07XG4gIGZvbnQtc2l6ZTogMC44MTI1cmVtO1xuICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICBsZXR0ZXItc3BhY2luZzogMC41cHg7XG59XG4ucmFpZi1hZG1pbiAudGFibGUgdGQge1xuICBwYWRkaW5nOiAxcmVtIDAuNzVyZW07XG4gIHZlcnRpY2FsLWFsaWduOiBtaWRkbGU7XG4gIGJvcmRlci1jb2xvcjogI2UyZThmMDtcbn1cbi5yYWlmLWFkbWluIC50YWJsZS50YWJsZS1ob3ZlciB0Ym9keSB0cjpob3ZlciB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmMGY5ZmY7XG59XG4ucmFpZi1hZG1pbiAudGFibGUudGFibGUtc3RyaXBlZCB0Ym9keSB0cjpudGgtb2YtdHlwZShvZGQpIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogcmdiYSgyNDAsIDI0OSwgMjU1LCAwLjUpO1xufVxuLnJhaWYtYWRtaW4gLnRhYmxlLXJlc3BvbnNpdmUge1xuICBib3JkZXItcmFkaXVzOiA0cHg7XG4gIGJveC1zaGFkb3c6IDAgNHB4IDZweCByZ2JhKDUwLCA1MCwgOTMsIDAuMTEpLCAwIDFweCAzcHggcmdiYSgwLCAwLCAwLCAwLjA4KTtcbiAgYmFja2dyb3VuZC1jb2xvcjogd2hpdGU7XG4gIG92ZXJmbG93OiBoaWRkZW47XG59XG4ucmFpZi1hZG1pbiAuYmFkZ2Uge1xuICBmb250LXdlaWdodDogNTAwO1xuICBwYWRkaW5nOiAwLjRlbSAwLjY1ZW07XG4gIGZvbnQtc2l6ZTogMC43NWVtO1xuICBib3JkZXItcmFkaXVzOiA1MHJlbTtcbn1cbi5yYWlmLWFkbWluIC5iYWRnZS5iZy1zdWNjZXNzIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogIzBlYTVlOSAhaW1wb3J0YW50O1xufVxuLnJhaWYtYWRtaW4gLmJhZGdlLmJnLWRhbmdlciB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNlZjQ0NDQgIWltcG9ydGFudDtcbn1cbi5yYWlmLWFkbWluIC5iYWRnZS5iZy13YXJuaW5nIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2Y1OWUwYiAhaW1wb3J0YW50O1xuICBjb2xvcjogd2hpdGUgIWltcG9ydGFudDtcbn1cbi5yYWlmLWFkbWluIC5iYWRnZS5iZy1zZWNvbmRhcnkge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjODg5OGFhICFpbXBvcnRhbnQ7XG59XG4ucmFpZi1hZG1pbiAuYnRuIHtcbiAgZm9udC13ZWlnaHQ6IDUwMDtcbiAgcGFkZGluZzogMC41cmVtIDFyZW07XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgdHJhbnNpdGlvbjogYWxsIDAuMnMgZWFzZTtcbiAgYm94LXNoYWRvdzogMCA0cHggNnB4IHJnYmEoNTAsIDUwLCA5MywgMC4xMSksIDAgMXB4IDNweCByZ2JhKDAsIDAsIDAsIDAuMDgpO1xufVxuLnJhaWYtYWRtaW4gLmJ0bi5idG4tb3V0bGluZS1zZWNvbmRhcnkge1xuICBjb2xvcjogIzUyNWY3ZjtcbiAgYm9yZGVyLWNvbG9yOiAjZTJlOGYwO1xufVxuLnJhaWYtYWRtaW4gLmJ0bi5idG4tb3V0bGluZS1zZWNvbmRhcnk6aG92ZXIge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjBmOWZmO1xuICBjb2xvcjogIzFlM2E4YTtcbiAgYm9yZGVyLWNvbG9yOiAjZTJlOGYwO1xufVxuLnJhaWYtYWRtaW4gcHJlIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2YwZjlmZjtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBwYWRkaW5nOiAxcmVtO1xuICBtYXJnaW4tYm90dG9tOiAwO1xuICBmb250LXNpemU6IDAuODc1cmVtO1xuICBjb2xvcjogIzFlM2E4YTtcbiAgYm9yZGVyOiAxcHggc29saWQgI2UyZThmMDtcbn1cbi5yYWlmLWFkbWluIC5wcmUtd3JhcCB7XG4gIHdoaXRlLXNwYWNlOiBwcmUtd3JhcDtcbn1cbi5yYWlmLWFkbWluIC5saXN0LWdyb3VwLWl0ZW0ge1xuICBib3JkZXItY29sb3I6ICNlMmU4ZjA7XG4gIHBhZGRpbmc6IDFyZW07XG59XG4ucmFpZi1hZG1pbiAubGlzdC1ncm91cC1pdGVtOmZpcnN0LWNoaWxkIHtcbiAgYm9yZGVyLXRvcC1sZWZ0LXJhZGl1czogNHB4O1xuICBib3JkZXItdG9wLXJpZ2h0LXJhZGl1czogNHB4O1xufVxuLnJhaWYtYWRtaW4gLmxpc3QtZ3JvdXAtaXRlbTpsYXN0LWNoaWxkIHtcbiAgYm9yZGVyLWJvdHRvbS1sZWZ0LXJhZGl1czogNHB4O1xuICBib3JkZXItYm90dG9tLXJpZ2h0LXJhZGl1czogNHB4O1xufVxuLnJhaWYtYWRtaW4gLnBhZ2luYXRpb24gLnBhZ2UtbGluayB7XG4gIGNvbG9yOiAjNTI1ZjdmO1xuICBib3JkZXItY29sb3I6ICNlMmU4ZjA7XG4gIHBhZGRpbmc6IDAuNXJlbSAwLjc1cmVtO1xufVxuLnJhaWYtYWRtaW4gLnBhZ2luYXRpb24gLnBhZ2UtbGluazpob3ZlciB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmMGY5ZmY7XG4gIGJvcmRlci1jb2xvcjogI2UyZThmMDtcbiAgY29sb3I6ICMxZTNhOGE7XG59XG4ucmFpZi1hZG1pbiAucGFnaW5hdGlvbiAucGFnZS1pdGVtLmFjdGl2ZSAucGFnZS1saW5rIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogIzNiODJmNjtcbiAgYm9yZGVyLWNvbG9yOiAjM2I4MmY2O1xufVxuLnJhaWYtYWRtaW4gLnJvdy5tYi0zIC5jb2wtbWQtMyBzdHJvbmcge1xuICBjb2xvcjogIzFlM2E4YTtcbn1cbi5yYWlmLWFkbWluIC5hbGVydCB7XG4gIGJvcmRlcjogbm9uZTtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBib3gtc2hhZG93OiAwIDRweCA2cHggcmdiYSg1MCwgNTAsIDkzLCAwLjExKSwgMCAxcHggM3B4IHJnYmEoMCwgMCwgMCwgMC4wOCk7XG59XG4ucmFpZi1hZG1pbiAuYWxlcnQuYWxlcnQtaW5mbyB7XG4gIGJhY2tncm91bmQtY29sb3I6IHJnYmEoNTksIDEzMCwgMjQ2LCAwLjEpO1xuICBjb2xvcjogIzNiODJmNjtcbn1cbi5yYWlmLWFkbWluIC50ZXh0LW11dGVkIHtcbiAgY29sb3I6ICM4ODk4YWEgIWltcG9ydGFudDtcbn1cbkBtZWRpYSAobWF4LXdpZHRoOiA3NjcuOThweCkge1xuICAucmFpZi1hZG1pbiAuc2lkZWJhciB7XG4gICAgcG9zaXRpb246IHN0YXRpYztcbiAgICBoZWlnaHQ6IGF1dG87XG4gICAgcGFkZGluZy10b3A6IDA7XG4gIH1cbiAgLnJhaWYtYWRtaW4gLnNpZGViYXIgLm5hdi1saW5rIHtcbiAgICBtYXJnaW46IDA7XG4gICAgYm9yZGVyLXJhZGl1czogMDtcbiAgfVxuICAucmFpZi1hZG1pbiBtYWluIHtcbiAgICBwYWRkaW5nLXRvcDogMXJlbTtcbiAgfVxufVxuIl19 */
282
+ /*# sourceMappingURL=data:application/json;base64,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RUFDaEIsb0JBQW9CO0VBQ3BCLGtCQUFrQjtFQUNsQix5QkFBeUI7RUFDekIsMkVBQTJFO0FBQzdFO0FBQ0E7RUFDRSxjQUFjO0VBQ2QscUJBQXFCO0FBQ3ZCO0FBQ0E7RUFDRSx5QkFBeUI7RUFDekIsY0FBYztFQUNkLHFCQUFxQjtBQUN2QjtBQUNBO0VBQ0UseUJBQXlCO0VBQ3pCLGtCQUFrQjtFQUNsQixhQUFhO0VBQ2IsZ0JBQWdCO0VBQ2hCLG1CQUFtQjtFQUNuQixjQUFjO0VBQ2QseUJBQXlCO0FBQzNCO0FBQ0E7RUFDRSxxQkFBcUI7QUFDdkI7QUFDQTtFQUNFLHFCQUFxQjtFQUNyQixhQUFhO0FBQ2Y7QUFDQTtFQUNFLDJCQUEyQjtFQUMzQiw0QkFBNEI7QUFDOUI7QUFDQTtFQUNFLDhCQUE4QjtFQUM5QiwrQkFBK0I7QUFDakM7QUFDQTtFQUNFLGNBQWM7RUFDZCxxQkFBcUI7RUFDckIsdUJBQXVCO0FBQ3pCO0FBQ0E7RUFDRSx5QkFBeUI7RUFDekIscUJBQXFCO0VBQ3JCLGNBQWM7QUFDaEI7QUFDQTtFQUNFLHlCQUF5QjtFQUN6QixxQkFBcUI7QUFDdkI7QUFDQTtFQUNFLGNBQWM7QUFDaEI7QUFDQTtFQUNFLFlBQVk7RUFDWixrQkFBa0I7RUFDbEIsMkVBQTJFO0FBQzdFO0FBQ0E7RUFDRSx5Q0FBeUM7RUFDekMsY0FBYztBQUNoQjtBQUNBO0VBQ0UseUJBQXlCO0FBQzNCO0FBQ0E7RUFDRTtJQUNFLGdCQUFnQjtJQUNoQixZQUFZO0lBQ1osY0FBYztFQUNoQjtFQUNBO0lBQ0UsU0FBUztJQUNULGdCQUFnQjtFQUNsQjtFQUNBO0lBQ0UsaUJBQWlCO0VBQ25CO0FBQ0YiLCJmaWxlIjoicmFpZl9hZG1pbi5jc3MiLCJzb3VyY2VzQ29udGVudCI6WyIuY29udmVyc2F0aW9uLWNhcmQgLmNhcmQtYm9keSB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmOGY5ZmE7XG59XG5cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZSB7XG4gIHBvc2l0aW9uOiByZWxhdGl2ZTtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZTpsYXN0LWNoaWxkIHtcbiAgbWFyZ2luLWJvdHRvbTogMDtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZS51c2VyIHtcbiAgYm9yZGVyLWxlZnQ6IDNweCBzb2xpZCAjM2I4MmY2O1xufVxuLmNvbnZlcnNhdGlvbi1oaXN0b3J5IC5tZXNzYWdlLnVzZXIgLm1lc3NhZ2UtY29udGVudCB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmMGY5ZmY7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UuYXNzaXN0YW50IHtcbiAgYm9yZGVyLWxlZnQ6IDNweCBzb2xpZCByZ2IoMjUsIDEzNSwgODQpO1xufVxuLmNvbnZlcnNhdGlvbi1oaXN0b3J5IC5tZXNzYWdlLmFzc2lzdGFudCAubWVzc2FnZS1jb250ZW50IHtcbiAgYmFja2dyb3VuZC1jb2xvcjogcmdiKDIzMiwgMjU1LCAyNDQpO1xufVxuLmNvbnZlcnNhdGlvbi1oaXN0b3J5IC5tZXNzYWdlLnRvb2xfY2FsbCB7XG4gIGJvcmRlci1sZWZ0OiAzcHggc29saWQgI2Y1OWUwYjtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZS50b29sX2NhbGwgLm1lc3NhZ2UtY29udGVudCB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmZmZiZWI7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UudG9vbF9jYWxsX3Jlc3VsdCB7XG4gIGJvcmRlci1sZWZ0OiAzcHggc29saWQgcmdiKDI1LCAxMzUsIDg0KTtcbn1cbi5jb252ZXJzYXRpb24taGlzdG9yeSAubWVzc2FnZS50b29sX2NhbGxfcmVzdWx0IC5tZXNzYWdlLWNvbnRlbnQge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2IoMjMyLCAyNTUsIDI0NCk7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UgLm1lc3NhZ2UtY29udGVudCB7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcbiAgYm94LXNoYWRvdzogMCAxcHggMnB4IHJnYmEoMCwgMCwgMCwgMC4wNSk7XG4gIGZvbnQtc2l6ZTogMC44NzVyZW07XG4gIGxpbmUtaGVpZ2h0OiAxLjU7XG4gIGNvbG9yOiAjMWUzYThhO1xufVxuLmNvbnZlcnNhdGlvbi1oaXN0b3J5IC5tZXNzYWdlIC5tZXNzYWdlLWNvbnRlbnQgY29kZSB7XG4gIGRpc3BsYXk6IGJsb2NrO1xuICBmb250LWZhbWlseTogU0ZNb25vLVJlZ3VsYXIsIE1lbmxvLCBNb25hY28sIENvbnNvbGFzLCBcIkxpYmVyYXRpb24gTW9ub1wiLCBcIkNvdXJpZXIgTmV3XCIsIG1vbm9zcGFjZTtcbiAgd2hpdGUtc3BhY2U6IHByZS13cmFwO1xuICB3b3JkLWJyZWFrOiBicmVhay13b3JkO1xuICBiYWNrZ3JvdW5kOiBub25lO1xuICBib3JkZXI6IG5vbmU7XG4gIHBhZGRpbmc6IDA7XG4gIGNvbG9yOiBpbmhlcml0O1xufVxuLmNvbnZlcnNhdGlvbi1oaXN0b3J5IC5tZXNzYWdlIC5tZXNzYWdlLWNvbnRlbnQ6bGFzdC1jaGlsZCB7XG4gIG1hcmdpbi1ib3R0b206IDA7XG59XG4uY29udmVyc2F0aW9uLWhpc3RvcnkgLm1lc3NhZ2UgLm1lc3NhZ2UtY29udGVudCAubWVzc2FnZS1sYWJlbCB7XG4gIGRpc3BsYXk6IGlubGluZS1ibG9jaztcbiAgZm9udC1zaXplOiAwLjc1cmVtO1xuICBmb250LXdlaWdodDogNjAwO1xuICB0ZXh0LXRyYW5zZm9ybTogdXBwZXJjYXNlO1xuICBsZXR0ZXItc3BhY2luZzogMC41cHg7XG59XG5cbmJvZHkucmFpZi1hZG1pbiB7XG4gIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsIFwiU2Vnb2UgVUlcIiwgUm9ib3RvLCBIZWx2ZXRpY2EsIEFyaWFsLCBzYW5zLXNlcmlmO1xuICBjb2xvcjogIzUyNWY3ZjtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZmFmYztcbiAgbGluZS1oZWlnaHQ6IDEuNTtcbiAgZm9udC1zaXplOiAwLjkzNzVyZW07XG59XG5cbi5yYWlmLWFkbWluIC5uYXZiYXItYnJhbmQtbG9nbyB7XG4gIHdpZHRoOiAxMDBweDtcbn1cbi5yYWlmLWFkbWluIGgxLFxuLnJhaWYtYWRtaW4gaDIsXG4ucmFpZi1hZG1pbiBoMyxcbi5yYWlmLWFkbWluIGg0LFxuLnJhaWYtYWRtaW4gaDUsXG4ucmFpZi1hZG1pbiBoNiB7XG4gIGNvbG9yOiAjMWUzYThhO1xuICBmb250LXdlaWdodDogNjAwO1xufVxuLnJhaWYtYWRtaW4gYSB7XG4gIGNvbG9yOiAjM2I4MmY2O1xuICB0ZXh0LWRlY29yYXRpb246IG5vbmU7XG4gIHRyYW5zaXRpb246IGFsbCAwLjJzIGVhc2U7XG59XG4ucmFpZi1hZG1pbiBhOmhvdmVyIHtcbiAgY29sb3I6IHJnYigxMS4xNTEyMTk1MTIyLCA5OS4xMjE5NTEyMTk1LCAyNDIuODQ4NzgwNDg3OCk7XG59XG4ucmFpZi1hZG1pbiAubmF2YmFyIHtcbiAgYm94LXNoYWRvdzogMCAycHggNXB4IDAgcmdiYSg1MCwgNTAsIDkzLCAwLjEpO1xuICBwYWRkaW5nOiAwLjc1cmVtIDEuNXJlbTtcbn1cbi5yYWlmLWFkbWluIC5uYXZiYXIubmF2YmFyLWRhcmsge1xuICBiYWNrZ3JvdW5kOiBsaW5lYXItZ3JhZGllbnQoMTM1ZGVnLCAjMWUzYThhLCByZ2IoMjAuODkyODU3MTQyOSwgNDAuMzkyODU3MTQyOSwgOTYuMTA3MTQyODU3MSkpICFpbXBvcnRhbnQ7XG59XG4ucmFpZi1hZG1pbiAubmF2YmFyIC5uYXZiYXItYnJhbmQge1xuICBmb250LXdlaWdodDogNzAwO1xuICBsZXR0ZXItc3BhY2luZzogMC41cHg7XG59XG4ucmFpZi1hZG1pbiAuc2lkZWJhciB7XG4gIGJveC1zaGFkb3c6IGluc2V0IC0xcHggMCAwICNlMmU4ZjA7XG4gIGJhY2tncm91bmQtY29sb3I6IHdoaXRlICFpbXBvcnRhbnQ7XG4gIHBhZGRpbmctdG9wOiAxcmVtO1xuICBoZWlnaHQ6IDEwMHZoO1xufVxuLnJhaWYtYWRtaW4gLnNpZGViYXIgLm5hdi1saW5rIHtcbiAgY29sb3I6ICM1MjVmN2Y7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgbWFyZ2luOiAwLjI1cmVtIDAuNzVyZW07XG4gIHBhZGRpbmc6IDAuNXJlbSAwLjc1cmVtO1xufVxuLnJhaWYtYWRtaW4gLnNpZGViYXIgLm5hdi1saW5rOmhvdmVyIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2YwZjlmZjtcbn1cbi5yYWlmLWFkbWluIC5zaWRlYmFyIC5uYXYtbGluay5hY3RpdmUge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDU5LCAxMzAsIDI0NiwgMC4xKTtcbiAgY29sb3I6ICMzYjgyZjY7XG4gIGZvbnQtd2VpZ2h0OiA1MDA7XG59XG4ucmFpZi1hZG1pbiBtYWluIHtcbiAgcGFkZGluZy10b3A6IDEuNXJlbTtcbiAgcGFkZGluZy1ib3R0b206IDNyZW07XG59XG4ucmFpZi1hZG1pbiAuY2FyZCB7XG4gIGJvcmRlcjogbm9uZTtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBib3gtc2hhZG93OiAwIDRweCA2cHggcmdiYSg1MCwgNTAsIDkzLCAwLjExKSwgMCAxcHggM3B4IHJnYmEoMCwgMCwgMCwgMC4wOCk7XG4gIG1hcmdpbi1ib3R0b206IDEuNXJlbTtcbiAgb3ZlcmZsb3c6IGhpZGRlbjtcbn1cbi5yYWlmLWFkbWluIC5jYXJkIC5jYXJkLWhlYWRlciB7XG4gIGJhY2tncm91bmQtY29sb3I6IHdoaXRlO1xuICBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2UyZThmMDtcbiAgcGFkZGluZzogMXJlbSAxLjI1cmVtO1xufVxuLnJhaWYtYWRtaW4gLmNhcmQgLmNhcmQtaGVhZGVyIGg1IHtcbiAgbWFyZ2luLWJvdHRvbTogMDtcbiAgZm9udC1zaXplOiAxcmVtO1xuICBmb250LXdlaWdodDogNjAwO1xufVxuLnJhaWYtYWRtaW4gLmNhcmQgLmNhcmQtYm9keSB7XG4gIHBhZGRpbmc6IDEuMjVyZW07XG59XG4ucmFpZi1hZG1pbiAuY2FyZC5jb252ZXJzYXRpb24tY2FyZCAuY2FyZC1ib2R5IHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2Y4ZjlmYTtcbn1cbi5yYWlmLWFkbWluIC50YWJsZSB7XG4gIG1hcmdpbi1ib3R0b206IDA7XG59XG4ucmFpZi1hZG1pbiAudGFibGUgdGgge1xuICBmb250LXdlaWdodDogNjAwO1xuICBjb2xvcjogIzFlM2E4YTtcbiAgYm9yZGVyLXRvcDogbm9uZTtcbiAgcGFkZGluZzogMXJlbSAwLjc1cmVtO1xuICBmb250LXNpemU6IDAuODEyNXJlbTtcbiAgdGV4dC10cmFuc2Zvcm06IHVwcGVyY2FzZTtcbiAgbGV0dGVyLXNwYWNpbmc6IDAuNXB4O1xufVxuLnJhaWYtYWRtaW4gLnRhYmxlIHRkIHtcbiAgcGFkZGluZzogMXJlbSAwLjc1cmVtO1xuICB2ZXJ0aWNhbC1hbGlnbjogbWlkZGxlO1xuICBib3JkZXItY29sb3I6ICNlMmU4ZjA7XG59XG4ucmFpZi1hZG1pbiAudGFibGUudGFibGUtaG92ZXIgdGJvZHkgdHI6aG92ZXIge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjBmOWZmO1xufVxuLnJhaWYtYWRtaW4gLnRhYmxlLnRhYmxlLXN0cmlwZWQgdGJvZHkgdHI6bnRoLW9mLXR5cGUob2RkKSB7XG4gIGJhY2tncm91bmQtY29sb3I6IHJnYmEoMjQwLCAyNDksIDI1NSwgMC41KTtcbn1cbi5yYWlmLWFkbWluIC50YWJsZS1yZXNwb25zaXZlIHtcbiAgYm9yZGVyLXJhZGl1czogNHB4O1xuICBib3gtc2hhZG93OiAwIDRweCA2cHggcmdiYSg1MCwgNTAsIDkzLCAwLjExKSwgMCAxcHggM3B4IHJnYmEoMCwgMCwgMCwgMC4wOCk7XG4gIGJhY2tncm91bmQtY29sb3I6IHdoaXRlO1xuICBvdmVyZmxvdzogaGlkZGVuO1xufVxuLnJhaWYtYWRtaW4gLmJhZGdlIHtcbiAgZm9udC13ZWlnaHQ6IDUwMDtcbiAgcGFkZGluZzogMC40ZW0gMC42NWVtO1xuICBmb250LXNpemU6IDAuNzVlbTtcbiAgYm9yZGVyLXJhZGl1czogNTByZW07XG59XG4ucmFpZi1hZG1pbiAuYmFkZ2UuYmctc3VjY2VzcyB7XG4gIGJhY2tncm91bmQtY29sb3I6ICMwZWE1ZTkgIWltcG9ydGFudDtcbn1cbi5yYWlmLWFkbWluIC5iYWRnZS5iZy1kYW5nZXIge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZWY0NDQ0ICFpbXBvcnRhbnQ7XG59XG4ucmFpZi1hZG1pbiAuYmFkZ2UuYmctd2FybmluZyB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmNTllMGIgIWltcG9ydGFudDtcbiAgY29sb3I6IHdoaXRlICFpbXBvcnRhbnQ7XG59XG4ucmFpZi1hZG1pbiAuYmFkZ2UuYmctc2Vjb25kYXJ5IHtcbiAgYmFja2dyb3VuZC1jb2xvcjogIzg4OThhYSAhaW1wb3J0YW50O1xufVxuLnJhaWYtYWRtaW4gLmJ0biB7XG4gIGZvbnQtd2VpZ2h0OiA1MDA7XG4gIHBhZGRpbmc6IDAuNXJlbSAxcmVtO1xuICBib3JkZXItcmFkaXVzOiA0cHg7XG4gIHRyYW5zaXRpb246IGFsbCAwLjJzIGVhc2U7XG4gIGJveC1zaGFkb3c6IDAgNHB4IDZweCByZ2JhKDUwLCA1MCwgOTMsIDAuMTEpLCAwIDFweCAzcHggcmdiYSgwLCAwLCAwLCAwLjA4KTtcbn1cbi5yYWlmLWFkbWluIC5idG4uYnRuLW91dGxpbmUtc2Vjb25kYXJ5IHtcbiAgY29sb3I6ICM1MjVmN2Y7XG4gIGJvcmRlci1jb2xvcjogI2UyZThmMDtcbn1cbi5yYWlmLWFkbWluIC5idG4uYnRuLW91dGxpbmUtc2Vjb25kYXJ5OmhvdmVyIHtcbiAgYmFja2dyb3VuZC1jb2xvcjogI2YwZjlmZjtcbiAgY29sb3I6ICMxZTNhOGE7XG4gIGJvcmRlci1jb2xvcjogI2UyZThmMDtcbn1cbi5yYWlmLWFkbWluIHByZSB7XG4gIGJhY2tncm91bmQtY29sb3I6ICNmMGY5ZmY7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgcGFkZGluZzogMXJlbTtcbiAgbWFyZ2luLWJvdHRvbTogMDtcbiAgZm9udC1zaXplOiAwLjg3NXJlbTtcbiAgY29sb3I6ICMxZTNhOGE7XG4gIGJvcmRlcjogMXB4IHNvbGlkICNlMmU4ZjA7XG59XG4ucmFpZi1hZG1pbiAucHJlLXdyYXAge1xuICB3aGl0ZS1zcGFjZTogcHJlLXdyYXA7XG59XG4ucmFpZi1hZG1pbiAubGlzdC1ncm91cC1pdGVtIHtcbiAgYm9yZGVyLWNvbG9yOiAjZTJlOGYwO1xuICBwYWRkaW5nOiAxcmVtO1xufVxuLnJhaWYtYWRtaW4gLmxpc3QtZ3JvdXAtaXRlbTpmaXJzdC1jaGlsZCB7XG4gIGJvcmRlci10b3AtbGVmdC1yYWRpdXM6IDRweDtcbiAgYm9yZGVyLXRvcC1yaWdodC1yYWRpdXM6IDRweDtcbn1cbi5yYWlmLWFkbWluIC5saXN0LWdyb3VwLWl0ZW06bGFzdC1jaGlsZCB7XG4gIGJvcmRlci1ib3R0b20tbGVmdC1yYWRpdXM6IDRweDtcbiAgYm9yZGVyLWJvdHRvbS1yaWdodC1yYWRpdXM6IDRweDtcbn1cbi5yYWlmLWFkbWluIC5wYWdpbmF0aW9uIC5wYWdlLWxpbmsge1xuICBjb2xvcjogIzUyNWY3ZjtcbiAgYm9yZGVyLWNvbG9yOiAjZTJlOGYwO1xuICBwYWRkaW5nOiAwLjVyZW0gMC43NXJlbTtcbn1cbi5yYWlmLWFkbWluIC5wYWdpbmF0aW9uIC5wYWdlLWxpbms6aG92ZXIge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiAjZjBmOWZmO1xuICBib3JkZXItY29sb3I6ICNlMmU4ZjA7XG4gIGNvbG9yOiAjMWUzYThhO1xufVxuLnJhaWYtYWRtaW4gLnBhZ2luYXRpb24gLnBhZ2UtaXRlbS5hY3RpdmUgLnBhZ2UtbGluayB7XG4gIGJhY2tncm91bmQtY29sb3I6ICMzYjgyZjY7XG4gIGJvcmRlci1jb2xvcjogIzNiODJmNjtcbn1cbi5yYWlmLWFkbWluIC5yb3cubWItMyAuY29sLW1kLTMgc3Ryb25nIHtcbiAgY29sb3I6ICMxZTNhOGE7XG59XG4ucmFpZi1hZG1pbiAuYWxlcnQge1xuICBib3JkZXI6IG5vbmU7XG4gIGJvcmRlci1yYWRpdXM6IDRweDtcbiAgYm94LXNoYWRvdzogMCA0cHggNnB4IHJnYmEoNTAsIDUwLCA5MywgMC4xMSksIDAgMXB4IDNweCByZ2JhKDAsIDAsIDAsIDAuMDgpO1xufVxuLnJhaWYtYWRtaW4gLmFsZXJ0LmFsZXJ0LWluZm8ge1xuICBiYWNrZ3JvdW5kLWNvbG9yOiByZ2JhKDU5LCAxMzAsIDI0NiwgMC4xKTtcbiAgY29sb3I6ICMzYjgyZjY7XG59XG4ucmFpZi1hZG1pbiAudGV4dC1tdXRlZCB7XG4gIGNvbG9yOiAjODg5OGFhICFpbXBvcnRhbnQ7XG59XG5AbWVkaWEgKG1heC13aWR0aDogNzY3Ljk4cHgpIHtcbiAgLnJhaWYtYWRtaW4gLnNpZGViYXIge1xuICAgIHBvc2l0aW9uOiBzdGF0aWM7XG4gICAgaGVpZ2h0OiBhdXRvO1xuICAgIHBhZGRpbmctdG9wOiAwO1xuICB9XG4gIC5yYWlmLWFkbWluIC5zaWRlYmFyIC5uYXYtbGluayB7XG4gICAgbWFyZ2luOiAwO1xuICAgIGJvcmRlci1yYWRpdXM6IDA7XG4gIH1cbiAgLnJhaWYtYWRtaW4gbWFpbiB7XG4gICAgcGFkZGluZy10b3A6IDFyZW07XG4gIH1cbn1cbiJdfQ== */
@@ -6,6 +6,6 @@ export default class extends Controller {
6
6
  }
7
7
 
8
8
  scrollToBottom() {
9
- this.element.scrollTo({ top: this.element.scrollHeight, behavior: "smooth" });
9
+ this.element.scrollTo({ top: this.element.scrollHeight });
10
10
  }
11
11
  }
@@ -29,6 +29,22 @@
29
29
  }
30
30
  }
31
31
 
32
+ &.tool_call {
33
+ border-left: 3px solid #f59e0b;
34
+
35
+ .message-content {
36
+ background-color: #fffbeb;
37
+ }
38
+ }
39
+
40
+ &.tool_call_result {
41
+ border-left: 3px solid rgba(25, 135, 84, 1);
42
+
43
+ .message-content {
44
+ background-color: rgb(232, 255, 244);
45
+ }
46
+ }
47
+
32
48
  .message-content {
33
49
  border-radius: 4px;
34
50
  overflow: hidden;
@@ -0,0 +1,3 @@
1
+ .raif-conversation-entries-container {
2
+ scroll-behavior: smooth;
3
+ }
@@ -1 +1,2 @@
1
- @use "raif/loader";
1
+ @use "raif/loader";
2
+ @use "raif/conversations";
@@ -29,6 +29,22 @@ module Raif
29
29
  24.hours.ago..Time.current
30
30
  end
31
31
  end
32
+
33
+ helper_method :conversation_message_header_class
34
+ def conversation_message_header_class(message)
35
+ message_type = message["type"] || message["role"]
36
+
37
+ case message_type
38
+ when "user"
39
+ "text-primary"
40
+ when "tool_call"
41
+ "text-warning"
42
+ when "tool_call_result"
43
+ "text-success"
44
+ else
45
+ "text-success"
46
+ end
47
+ end
32
48
  end
33
49
  end
34
50
  end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Admin
5
+ class ConfigsController < Raif::Admin::ApplicationController
6
+
7
+ def show
8
+ @config = Raif.config
9
+ @config_settings = build_config_settings
10
+ end
11
+
12
+ private
13
+
14
+ def build_config_settings
15
+ [
16
+ { key: "agent_types", value: format_array(@config.agent_types.to_a) },
17
+ { key: "anthropic_api_key", value: mask_sensitive_value(@config.anthropic_api_key) },
18
+ { key: "anthropic_models_enabled", value: @config.anthropic_models_enabled },
19
+ { key: "authorize_admin_controller_action", value: format_proc(@config.authorize_admin_controller_action) },
20
+ { key: "authorize_controller_action", value: format_proc(@config.authorize_controller_action) },
21
+ { key: "aws_bedrock_model_name_prefix", value: @config.aws_bedrock_model_name_prefix },
22
+ { key: "aws_bedrock_region", value: @config.aws_bedrock_region },
23
+ { key: "bedrock_embedding_models_enabled", value: @config.bedrock_embedding_models_enabled },
24
+ { key: "bedrock_models_enabled", value: @config.bedrock_models_enabled },
25
+ { key: "conversation_entries_controller", value: @config.conversation_entries_controller },
26
+ { key: "conversation_llm_messages_max_length_default", value: @config.conversation_llm_messages_max_length_default },
27
+ { key: "conversation_system_prompt_intro", value: truncate_text(@config.conversation_system_prompt_intro, 100) },
28
+ { key: "conversation_types", value: format_array(@config.conversation_types.to_a) },
29
+ { key: "conversations_controller", value: @config.conversations_controller },
30
+ { key: "current_user_method", value: @config.current_user_method },
31
+ { key: "default_embedding_model_key", value: @config.default_embedding_model_key },
32
+ { key: "default_llm_model_key", value: @config.default_llm_model_key },
33
+ { key: "evals_default_llm_judge_model_key", value: @config.evals_default_llm_judge_model_key },
34
+ { key: "evals_verbose_output", value: @config.evals_verbose_output },
35
+ { key: "google_api_key", value: mask_sensitive_value(@config.google_api_key) },
36
+ { key: "google_models_enabled", value: @config.google_models_enabled },
37
+ { key: "llm_api_requests_enabled", value: @config.llm_api_requests_enabled },
38
+ { key: "llm_request_max_retries", value: @config.llm_request_max_retries },
39
+ { key: "llm_request_retriable_exceptions", value: @config.llm_request_retriable_exceptions.map(&:name).join(", ") },
40
+ { key: "model_superclass", value: @config.model_superclass },
41
+ { key: "open_ai_api_key", value: mask_sensitive_value(@config.open_ai_api_key) },
42
+ { key: "open_ai_api_version", value: @config.open_ai_api_version },
43
+ { key: "open_ai_auth_header_style", value: @config.open_ai_auth_header_style },
44
+ { key: "open_ai_base_url", value: @config.open_ai_base_url },
45
+ { key: "open_ai_embedding_base_url", value: @config.open_ai_embedding_base_url },
46
+ { key: "open_ai_embedding_models_enabled", value: @config.open_ai_embedding_models_enabled },
47
+ { key: "open_ai_models_enabled", value: @config.open_ai_models_enabled },
48
+ { key: "open_router_api_key", value: mask_sensitive_value(@config.open_router_api_key) },
49
+ { key: "open_router_app_name", value: @config.open_router_app_name },
50
+ { key: "open_router_models_enabled", value: @config.open_router_models_enabled },
51
+ { key: "open_router_site_url", value: @config.open_router_site_url },
52
+ { key: "request_open_timeout", value: @config.request_open_timeout },
53
+ { key: "request_read_timeout", value: @config.request_read_timeout },
54
+ { key: "request_write_timeout", value: @config.request_write_timeout },
55
+ { key: "streaming_update_chunk_size_threshold", value: @config.streaming_update_chunk_size_threshold },
56
+ { key: "task_creator_optional", value: @config.task_creator_optional },
57
+ { key: "task_system_prompt_intro", value: truncate_text(@config.task_system_prompt_intro, 100) },
58
+ { key: "user_tool_types", value: format_array(@config.user_tool_types) }
59
+ ]
60
+ end
61
+
62
+ def mask_sensitive_value(value)
63
+ return "Not set" if value.blank?
64
+ return "Not set" if value.include?("placeholder")
65
+
66
+ "#{value[0...5]}#{"*" * 20}"
67
+ end
68
+
69
+ def format_proc(value)
70
+ return "Not set" unless value.respond_to?(:call)
71
+
72
+ source = value.source_location
73
+ if source
74
+ "Lambda/Proc defined at #{source[0]}:#{source[1]}"
75
+ else
76
+ "Lambda/Proc (source unavailable)"
77
+ end
78
+ end
79
+
80
+ def truncate_text(text, length)
81
+ return "Not set" if text.blank?
82
+ return format_proc(text) if text.respond_to?(:call)
83
+
84
+ text.length > length ? "#{text[0...length]}..." : text
85
+ end
86
+
87
+ def format_array(array)
88
+ return "None" if array.blank?
89
+
90
+ array.join(", ")
91
+ end
92
+ end
93
+ end
94
+ end
@@ -6,7 +6,24 @@ module Raif
6
6
  include Pagy::Backend
7
7
 
8
8
  def index
9
- @pagy, @model_completions = pagy(Raif::ModelCompletion.order(created_at: :desc))
9
+ @selected_status = params[:status].present? ? params[:status].to_sym : :all
10
+
11
+ model_completions = Raif::ModelCompletion.order(created_at: :desc)
12
+
13
+ if @selected_status.present? && @selected_status != :all
14
+ case @selected_status
15
+ when :completed
16
+ model_completions = model_completions.completed
17
+ when :failed
18
+ model_completions = model_completions.failed
19
+ when :started
20
+ model_completions = model_completions.started.where(completed_at: nil, failed_at: nil)
21
+ when :pending
22
+ model_completions = model_completions.where(started_at: nil)
23
+ end
24
+ end
25
+
26
+ @pagy, @model_completions = pagy(model_completions)
10
27
  end
11
28
 
12
29
  def show
@@ -6,7 +6,13 @@ module Raif
6
6
  include Pagy::Backend
7
7
 
8
8
  def index
9
- @pagy, @model_tool_invocations = pagy(Raif::ModelToolInvocation.order(created_at: :desc))
9
+ @tool_types = Raif::ModelToolInvocation.distinct.pluck(:tool_type)
10
+ @selected_type = params[:tool_types].present? && @tool_types.include?(params[:tool_types]) ? params[:tool_types] : "all"
11
+
12
+ model_tool_invocations = Raif::ModelToolInvocation.newest_first
13
+ model_tool_invocations = model_tool_invocations.where(tool_type: @selected_type) if @selected_type.present? && @selected_type != "all"
14
+
15
+ @pagy, @model_tool_invocations = pagy(model_tool_invocations)
10
16
  end
11
17
 
12
18
  def show
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Admin
5
+ module Stats
6
+ class ModelToolInvocationsController < Raif::Admin::ApplicationController
7
+ def index
8
+ @selected_period = params[:period] || "day"
9
+ @time_range = get_time_range(@selected_period)
10
+
11
+ @model_tool_invocation_count = Raif::ModelToolInvocation.where(created_at: @time_range).count
12
+
13
+ @model_tool_invocation_stats_by_type = Raif::ModelToolInvocation
14
+ .where(created_at: @time_range)
15
+ .group(:tool_type)
16
+ .count
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -7,17 +7,26 @@ module Raif
7
7
  def index
8
8
  @selected_period = params[:period] || "day"
9
9
  @time_range = get_time_range(@selected_period)
10
+ @show_model_breakdown = params[:show_model_breakdown] == "1"
10
11
 
11
12
  @task_count = Raif::Task.where(created_at: @time_range).count
12
13
 
13
- # Get task counts by type
14
- @task_counts_by_type = Raif::Task.where(created_at: @time_range).group(:type).count
14
+ group_columns = @show_model_breakdown ? [:type, :llm_model_key] : [:type]
15
+ select_columns = ["raif_tasks.type"]
16
+ select_columns << "raif_tasks.llm_model_key" if @show_model_breakdown
17
+ select_columns << "COUNT(raif_tasks.id)"
18
+ select_columns << "SUM(raif_model_completions.prompt_token_cost)"
19
+ select_columns << "SUM(raif_model_completions.output_token_cost)"
20
+ select_columns << "SUM(raif_model_completions.total_cost)"
15
21
 
16
- # Get costs by task type
17
- @task_costs_by_type = Raif::Task.joins(:raif_model_completion)
22
+ @task_stats_by_type = Raif::Task.joins(:raif_model_completion)
18
23
  .where(created_at: @time_range)
19
- .group(:type)
20
- .sum("raif_model_completions.total_cost")
24
+ .group(*group_columns)
25
+ .pluck(*select_columns)
26
+ .map do |type, *rest|
27
+ llm_model_key = @show_model_breakdown ? rest.shift : nil
28
+ Raif::Admin::TaskStat.new(type, llm_model_key, *rest)
29
+ end
21
30
  end
22
31
  end
23
32
  end
@@ -7,12 +7,41 @@ module Raif
7
7
  @selected_period = params[:period] || "day"
8
8
  @time_range = get_time_range(@selected_period)
9
9
 
10
- @model_completion_count = Raif::ModelCompletion.where(created_at: @time_range).count
11
- @model_completion_total_cost = Raif::ModelCompletion.where(created_at: @time_range).sum(:total_cost)
12
- @task_count = Raif::Task.where(created_at: @time_range).count
10
+ model_completions = Raif::ModelCompletion.where(created_at: @time_range)
11
+
12
+ @model_completion_count = model_completions.count
13
+ @model_completion_total_cost = model_completions.sum(:total_cost)
14
+ @model_completion_input_token_cost = model_completions.sum(:prompt_token_cost)
15
+ @model_completion_output_token_cost = model_completions.sum(:output_token_cost)
16
+
17
+ tasks = Raif::Task.where(created_at: @time_range)
18
+ @task_count = tasks.count
19
+ @task_total_cost = Raif::ModelCompletion.where(source_type: "Raif::Task", created_at: @time_range).sum(:total_cost)
20
+ @task_input_token_cost = Raif::ModelCompletion.where(source_type: "Raif::Task", created_at: @time_range).sum(:prompt_token_cost)
21
+ @task_output_token_cost = Raif::ModelCompletion.where(source_type: "Raif::Task", created_at: @time_range).sum(:output_token_cost)
22
+
13
23
  @conversation_count = Raif::Conversation.where(created_at: @time_range).count
24
+
14
25
  @conversation_entry_count = Raif::ConversationEntry.where(created_at: @time_range).count
26
+ @conversation_entry_total_cost = Raif::ModelCompletion.where(
27
+ source_type: "Raif::ConversationEntry",
28
+ created_at: @time_range,
29
+ ).sum(:total_cost)
30
+ @conversation_entry_input_token_cost = Raif::ModelCompletion.where(
31
+ source_type: "Raif::ConversationEntry",
32
+ created_at: @time_range,
33
+ ).sum(:prompt_token_cost)
34
+ @conversation_entry_output_token_cost = Raif::ModelCompletion.where(
35
+ source_type: "Raif::ConversationEntry",
36
+ created_at: @time_range,
37
+ ).sum(:output_token_cost)
38
+
15
39
  @agent_count = Raif::Agent.where(created_at: @time_range).count
40
+ @agent_total_cost = Raif::ModelCompletion.where(source_type: "Raif::Agent", created_at: @time_range).sum(:total_cost)
41
+ @agent_input_token_cost = Raif::ModelCompletion.where(source_type: "Raif::Agent", created_at: @time_range).sum(:prompt_token_cost)
42
+ @agent_output_token_cost = Raif::ModelCompletion.where(source_type: "Raif::Agent", created_at: @time_range).sum(:output_token_cost)
43
+
44
+ @model_tool_invocation_count = Raif::ModelToolInvocation.where(created_at: @time_range).count
16
45
  end
17
46
  end
18
47
  end
@@ -25,6 +25,7 @@ class Raif::ConversationEntriesController < Raif::ApplicationController
25
25
  @conversation_entry.creator = current_user
26
26
 
27
27
  if @conversation_entry.save
28
+ @conversation.update_columns(generating_entry_response: true)
28
29
  Raif::ConversationEntryJob.perform_later(conversation_entry: @conversation_entry)
29
30
  end
30
31
  end
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Raif::ConversationsController < Raif::ApplicationController
4
- before_action :validate_conversation_type
4
+ before_action :validate_conversation_type, unless: ->{ params[:action] == "index" && params[:conversation_type].blank? }
5
+
6
+ def index
7
+ @conversations = conversations_scope
8
+ end
5
9
 
6
10
  def show
7
11
  @conversations = conversations_scope
@@ -26,7 +30,11 @@ private
26
30
  end
27
31
 
28
32
  def conversations_scope
29
- raif_conversation_type.newest_first.where(creator: raif_current_user)
33
+ if params[:conversation_type].present?
34
+ raif_conversation_type
35
+ else
36
+ Raif::Conversation
37
+ end.newest_first.where(creator: raif_current_user)
30
38
  end
31
39
 
32
40
  def conversation_type_param
@@ -6,22 +6,24 @@ module Raif
6
6
  before_enqueue do |job|
7
7
  conversation_entry = job.arguments.first[:conversation_entry]
8
8
  conversation_entry.update_columns(started_at: Time.current)
9
+
10
+ unless conversation_entry.raif_conversation.generating_entry_response?
11
+ conversation_entry.raif_conversation.update_columns(generating_entry_response: true)
12
+ end
9
13
  end
10
14
 
11
15
  def perform(conversation_entry:)
12
16
  conversation = conversation_entry.raif_conversation
13
17
  conversation_entry.process_entry!
14
- conversation_entry.broadcast_replace_to conversation
15
18
 
16
- Turbo::StreamsChannel.broadcast_action_to(
17
- conversation,
18
- action: :raif_scroll_to_bottom,
19
- target: dom_id(conversation, :entries)
20
- )
19
+ Turbo::StreamsChannel.broadcast_render_to conversation,
20
+ partial: "raif/conversations/entry_processed",
21
+ locals: { conversation: conversation, conversation_entry: conversation_entry }
21
22
  rescue StandardError => e
22
23
  logger.error "Error processing conversation entry: #{e.message}"
23
24
  logger.error e.backtrace.join("\n")
24
25
 
26
+ conversation_entry.raif_conversation.update_columns(generating_entry_response: false)
25
27
  conversation_entry.failed!
26
28
  conversation_entry.broadcast_replace_to conversation
27
29
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Raif
4
+ module Admin
5
+ TaskStat = Data.define(:type, :llm_model_key, :count, :input_cost, :output_cost, :total_cost)
6
+ end
7
+ end
@@ -1,18 +1,55 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # == Schema Information
4
+ #
5
+ # Table name: raif_agents
6
+ #
7
+ # id :bigint not null, primary key
8
+ # available_model_tools :jsonb not null
9
+ # completed_at :datetime
10
+ # conversation_history :jsonb not null
11
+ # creator_type :string not null
12
+ # failed_at :datetime
13
+ # failure_reason :text
14
+ # final_answer :text
15
+ # iteration_count :integer default(0), not null
16
+ # llm_model_key :string not null
17
+ # max_iterations :integer default(10), not null
18
+ # requested_language_key :string
19
+ # run_with :jsonb
20
+ # source_type :string
21
+ # started_at :datetime
22
+ # system_prompt :text
23
+ # task :text
24
+ # type :string not null
25
+ # created_at :datetime not null
26
+ # updated_at :datetime not null
27
+ # creator_id :bigint not null
28
+ # source_id :bigint
29
+ #
30
+ # Indexes
31
+ #
32
+ # index_raif_agents_on_created_at (created_at)
33
+ # index_raif_agents_on_creator (creator_type,creator_id)
34
+ # index_raif_agents_on_source (source_type,source_id)
35
+ #
3
36
  module Raif
4
37
  class Agent < ApplicationRecord
5
38
  include Raif::Concerns::HasLlm
6
39
  include Raif::Concerns::HasRequestedLanguage
7
40
  include Raif::Concerns::HasAvailableModelTools
8
41
  include Raif::Concerns::InvokesModelTools
42
+ include Raif::Concerns::AgentInferenceStats
43
+ include Raif::Concerns::RunWith
9
44
 
10
45
  belongs_to :creator, polymorphic: true
46
+ belongs_to :source, polymorphic: true, optional: true
11
47
 
12
48
  has_many :raif_model_completions, as: :source, dependent: :destroy, class_name: "Raif::ModelCompletion"
13
49
 
14
50
  after_initialize -> { self.available_model_tools ||= [] }
15
51
  after_initialize -> { self.conversation_history ||= [] }
52
+ after_initialize -> { self.run_with ||= {} }
16
53
 
17
54
  boolean_timestamp :started_at
18
55
  boolean_timestamp :completed_at
@@ -69,16 +106,23 @@ module Raif
69
106
  Task: #{task}
70
107
  DEBUG
71
108
 
72
- add_conversation_history_entry({ role: "user", content: task })
109
+ add_conversation_history_entry(Raif::Messages::UserMessage.new(content: task).to_h)
73
110
 
74
111
  while iteration_count < max_iterations
75
112
  update_columns(iteration_count: iteration_count + 1)
76
113
 
114
+ # Update the system prompt on each iteration in case it has changed since the last iteration
115
+ self.system_prompt = build_system_prompt
116
+
117
+ # Hook for subclasses to perform actions before the LLM chat (e.g., add warnings)
118
+ before_iteration_llm_chat
119
+
77
120
  model_completion = llm.chat(
78
121
  messages: conversation_history,
79
122
  source: self,
80
123
  system_prompt: system_prompt,
81
- available_model_tools: native_model_tools
124
+ available_model_tools: native_model_tools,
125
+ tool_choice: tool_choice_for_iteration
82
126
  )
83
127
 
84
128
  logger.debug <<~DEBUG
@@ -106,6 +150,10 @@ module Raif
106
150
  raise
107
151
  end
108
152
 
153
+ def final_iteration?
154
+ iteration_count == max_iterations
155
+ end
156
+
109
157
  private
110
158
 
111
159
  def populate_default_model_tools
@@ -120,6 +168,19 @@ module Raif
120
168
  # no-op by default
121
169
  end
122
170
 
171
+ # Hook for subclasses to perform actions before the LLM chat on each iteration
172
+ # Override in subclasses to add warnings, context, etc.
173
+ def before_iteration_llm_chat
174
+ # no-op by default
175
+ end
176
+
177
+ # Hook for subclasses to specify tool_choice for the current iteration
178
+ # Override in subclasses to force specific tools (e.g., on final iteration)
179
+ # @return [Class, nil] A model tool class (e.g., Raif::ModelTools::AgentFinalAnswer), or nil for default behavior
180
+ def tool_choice_for_iteration
181
+ nil
182
+ end
183
+
123
184
  def add_conversation_history_entry(entry)
124
185
  entry_stringified = entry.stringify_keys
125
186
  conversation_history << entry_stringified