justinxzhao commited on
Commit
577870e
·
1 Parent(s): 3703473

Some refactoring, judging responses for direct assessment.

Browse files
Files changed (6) hide show
  1. app.py +227 -120
  2. app2.py +52 -0
  3. constants.py +119 -0
  4. judging.py +28 -0
  5. judging_dataclasses.py +28 -0
  6. prompts.py +150 -0
app.py CHANGED
@@ -7,6 +7,15 @@ import anthropic
7
  from together import Together
8
  import google.generativeai as genai
9
  import time
 
 
 
 
 
 
 
 
 
10
 
11
  dotenv.load_dotenv()
12
 
@@ -31,31 +40,6 @@ openai_client = OpenAI(
31
  # anthropic_client = anthropic.Client(api_key=ANTHROPIC_API_KEY)
32
  anthropic_client = anthropic.Anthropic()
33
 
34
- LLM_COUNCIL_MEMBERS = {
35
- "Smalls": [
36
- "openai://gpt-4o-mini",
37
- "together://meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
38
- "vertex://gemini-1.5-flash-001",
39
- "anthropic://claude-3-haiku-20240307",
40
- ],
41
- "Flagships": [
42
- "openai://gpt-4",
43
- "together://meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
44
- "vertex://gemini-1.5-pro-001",
45
- "anthropic://claude-3-5-sonnet",
46
- ],
47
- }
48
-
49
- PROVIDER_TO_AVATAR_MAP = {
50
- "openai://gpt-4o-mini": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIwLjk5ZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjU2IDI2MCI+PHBhdGggZD0iTTIzOS4xODQgMTA2LjIwM2E2NC43MiA2NC43MiAwIDAgMC01LjU3Ni01My4xMDNDMjE5LjQ1MiAyOC40NTkgMTkxIDE1Ljc4NCAxNjMuMjEzIDIxLjc0QTY1LjU4NiA2NS41ODYgMCAwIDAgNTIuMDk2IDQ1LjIyYTY0LjcyIDY0LjcyIDAgMCAwLTQzLjIzIDMxLjM2Yy0xNC4zMSAyNC42MDItMTEuMDYxIDU1LjYzNCA4LjAzMyA3Ni43NGE2NC42NyA2NC42NyAwIDAgMCA1LjUyNSA1My4xMDJjMTQuMTc0IDI0LjY1IDQyLjY0NCAzNy4zMjQgNzAuNDQ2IDMxLjM2YTY0LjcyIDY0LjcyIDAgMCAwIDQ4Ljc1NCAyMS43NDRjMjguNDgxLjAyNSA1My43MTQtMTguMzYxIDYyLjQxNC00NS40ODFhNjQuNzcgNjQuNzcgMCAwIDAgNDMuMjI5LTMxLjM2YzE0LjEzNy0yNC41NTggMTAuODc1LTU1LjQyMy04LjA4My03Ni40ODNtLTk3LjU2IDEzNi4zMzhhNDguNCA0OC40IDAgMCAxLTMxLjEwNS0xMS4yNTVsMS41MzUtLjg3bDUxLjY3LTI5LjgyNWE4LjYgOC42IDAgMCAwIDQuMjQ3LTcuMzY3di03Mi44NWwyMS44NDUgMTIuNjM2Yy4yMTguMTExLjM3LjMyLjQwOS41NjN2NjAuMzY3Yy0uMDU2IDI2LjgxOC0yMS43ODMgNDguNTQ1LTQ4LjYwMSA0OC42MDFNMzcuMTU4IDE5Ny45M2E0OC4zNSA0OC4zNSAwIDAgMS01Ljc4MS0zMi41ODlsMS41MzQuOTIxbDUxLjcyMiAyOS44MjZhOC4zNCA4LjM0IDAgMCAwIDguNDQxIDBsNjMuMTgxLTM2LjQyNXYyNS4yMjFhLjg3Ljg3IDAgMCAxLS4zNTguNjY1bC01Mi4zMzUgMzAuMTg0Yy0yMy4yNTcgMTMuMzk4LTUyLjk3IDUuNDMxLTY2LjQwNC0xNy44MDNNMjMuNTQ5IDg1LjM4YTQ4LjUgNDguNSAwIDAgMSAyNS41OC0yMS4zMzN2NjEuMzlhOC4yOSA4LjI5IDAgMCAwIDQuMTk1IDcuMzE2bDYyLjg3NCAzNi4yNzJsLTIxLjg0NSAxMi42MzZhLjgyLjgyIDAgMCAxLS43NjcgMEw0MS4zNTMgMTUxLjUzYy0yMy4yMTEtMTMuNDU0LTMxLjE3MS00My4xNDQtMTcuODA0LTY2LjQwNXptMTc5LjQ2NiA0MS42OTVsLTYzLjA4LTM2LjYzTDE2MS43MyA3Ny44NmEuODIuODIgMCAwIDEgLjc2OCAwbDUyLjIzMyAzMC4xODRhNDguNiA0OC42IDAgMCAxLTcuMzE2IDg3LjYzNXYtNjEuMzkxYTguNTQgOC41NCAwIDAgMC00LjQtNy4yMTNtMjEuNzQyLTMyLjY5bC0xLjUzNS0uOTIybC01MS42MTktMzAuMDgxYTguMzkgOC4zOSAwIDAgMC04LjQ5MiAwTDk5Ljk4IDk5LjgwOFY3NC41ODdhLjcyLjcyIDAgMCAxIC4zMDctLjY2NWw1Mi4yMzMtMzAuMTMzYTQ4LjY1MiA0OC42NTIgMCAwIDEgNzIuMjM2IDUwLjM5MXpNODguMDYxIDEzOS4wOTdsLTIxLjg0NS0xMi41ODVhLjg3Ljg3IDAgMCAxLS40MS0uNjE0VjY1LjY4NWE0OC42NTIgNDguNjUyIDAgMCAxIDc5Ljc1Ny0zNy4zNDZsLTEuNTM1Ljg3bC01MS42NyAyOS44MjVhOC42IDguNiAwIDAgMC00LjI0NiA3LjM2N3ptMTEuODY4LTI1LjU4TDEyOC4wNjcgOTcuM2wyOC4xODggMTYuMjE4djMyLjQzNGwtMjguMDg2IDE2LjIxOGwtMjguMTg4LTE2LjIxOHoiLz48L3N2Zz4=",
51
- "anthropic://claude-3-5-sonnet": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTE3LjMwNCAzLjU0MWgtMy42NzJsNi42OTYgMTYuOTE4SDI0Wm0tMTAuNjA4IDBMMCAyMC40NTloMy43NDRsMS4zNy0zLjU1M2g3LjAwNWwxLjM2OSAzLjU1M2gzLjc0NEwxMC41MzYgMy41NDFabS0uMzcxIDEwLjIyM0w4LjYxNiA3LjgybDIuMjkxIDUuOTQ1WiIvPjwvc3ZnPg==",
52
- "vertex://gemini-1.5-flash-001": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM0MjY4ZmYiIGQ9Ik0yNCAxMi4wMjRjLTYuNDM3LjM4OC0xMS41OSA1LjUzOS0xMS45NzcgMTEuOTc2aC0uMDQ3QzExLjU4OCAxNy41NjMgNi40MzYgMTIuNDEyIDAgMTIuMDI0di0uMDQ3QzYuNDM3IDExLjU4OCAxMS41ODggNi40MzcgMTEuOTc2IDBoLjA0N2MuMzg4IDYuNDM3IDUuNTQgMTEuNTg4IDExLjk3NyAxMS45Nzd6Ii8+PC9zdmc+",
53
- "together://meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGZpbGw9IiNiNGFjYmMiIGQ9Ik0yMC44NzEgMjQuNzh2LTYuMDZoMy4wMXY1Ljc3YzAgLjM0LS4xMi42Mi0uMzEuOTRsLTIuNDEgNC4yYy0uMTguMjMtLjQ1LjM3LS43NS4zN2gtMS4wM2MtLjIzIDAtLjM4LS4yNC0uMjgtLjQ1bDEuNjctNC4zNWMuMDctLjEzLjEtLjI3LjEtLjQyTTE3LjA5MSAzMGMuMiAwIC4yNi0uMjEuMjItLjM4bC0yLjMyLTguNjFoLTIuOTlsLjg1IDMuNTVjLjE5LjcxLjY2IDEuMzIgMS4zIDEuNjljLjE0LjA4LjI1LjIyLjI5LjM4bC44NyAzLjE0Yy4wNC4xNy4yMS4yMi4zOC4yMnoiLz48cGF0aCBmaWxsPSIjY2RjNGQ2IiBkPSJtMjguNTQxIDIzLjA4bC0xLjI3LS45NmEuOTQuOTQgMCAwIDEtLjI3LS42NnYtMi43NWMwLS42NC0uMTYtMS4yMzgtLjQ0Mi0xLjc2cS4yMTMuMDUuNDQyLjA1YTIgMiAwIDEgMC0xLjk0OS0xLjU0OWEzLjggMy44IDAgMCAwLTEuOC0uNDUxaC04LjE3Yy0uNjYgMC0xLjI3LS40Mi0xLjU3LTEuMDFMMTAuMDQxIDMuNWEyLjIzIDIuMjMgMCAwIDAtMi4xLTEuNWMtLjE4IDAtLjMuMTctLjI0LjM0TDguNTcxIDVjLS4yIDAtMS4wNy4yMy0xLjg1LjczbC0uODA2LjQ5OEw3LjAwMiAxMHY4LjI2YzAgMi4wMSAxLjI1IDMuNzIgMy4wMSA0LjQxdjcuMDJjLS4wMS4xNy4xMy4zMS4zLjMxaDEuMzdjLjE4IDAgLjMyLS4xNC4zMi0uMzF2LTEuOTZjMC0uMTcuMDctLjMyLjE4LS40NGMuNTMtLjUyLjgyLTEuMjMuODItMS45N1YyM2g1LjA3YzEuMjcgMCAyLjQ5LS41NSAzLjMzLTEuNWMwIC45NC40OCAxLjcyIDEuMzggMi4zMmwzLjk2IDIuNDNjLjE2LjExLjI2LjMuMjYuNXYyLjkzYzAgLjE3LjE0LjMxLjMxLjMxaDEuMzdjLjE3IDAgLjMxLS4xNC4zMS0uMzF2LTUuNTFjLjAxLS40LS4xNS0uOC0uNDUtMS4wOSIvPjxwYXRoIGZpbGw9IiNmM2FkNjEiIGQ9Ik02Ljg0MSA2Yy0uMzYgMC0uNzIuMS0xLjAzLjI5bC0yLjE5IDEuMzVjLS4zNi4yMy0uNi42MS0uNjIgMS4wM2MtLjAzLjczLjU1IDEuMzMgMS4yNyAxLjMzaDMuNDljLjU3IDAgMS4wNC0uNDcgMS4wNC0xLjA1di0xYzAtMS4wNy0uODgtMS45NS0xLjk2LTEuOTUiLz48cGF0aCBmaWxsPSIjMWMxYzFjIiBkPSJNNi41IDhhLjUuNSAwIDEgMCAwLTFhLjUuNSAwIDAgMCAwIDFtLTEuOTk5LjVjMC0uMjgtLjIyLS41LS41LS41aC0uNzZhMS4yIDEuMiAwIDAgMC0uMjEgMWguOTdjLjI4IDAgLjUtLjIyLjUtLjUiLz48cGF0aCBmaWxsPSIjZjNhZDYxIiBkPSJNMjguOTkxIDI4aC0xLjk5djEuNjhjMCAuMTcuMTQuMzEuMzEuMzFoMS4zN2MuMTcgMCAuMzEtLjE0LjMxLS4zMXptLTE2Ljk5IDBoLTEuOTl2MS42OWMtLjAxLjE3LjEzLjMxLjMuMzFoMS4zN2MuMTggMCAuMzItLjE0LjMyLS4zMXptNS4wODggMmwtMS4zOTgtLjAxYy0uMTcgMC0uMzQtLjA1LS4zOC0uMjJsLS40OS0xLjc3aDIuMDU0bC40MzYgMS42MmMuMDQuMTctLjAyLjM3OC0uMjE2LjM4em0yLjI4OCAwYS4zMTMuMzEzIDAgMCAxLS4yNzYtLjQ1bC41OTUtMS41NWgyLjRsLS45MzUgMS42M2EuOTUuOTUgMCAwIDEtLjc0Mi4zN3oiLz48L2c+PC9zdmc+",
54
- "anthropic://claude-3-haiku-20240307": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTE3LjMwNCAzLjU0MWgtMy42NzJsNi42OTYgMTYuOTE4SDI0Wm0tMTAuNjA4IDBMMCAyMC40NTloMy43NDRsMS4zNy0zLjU1M2g3LjAwNWwxLjM2OSAzLjU1M2gzLjc0NEwxMC41MzYgMy41NDFabS0uMzcxIDEwLjIyM0w4LjYxNiA3LjgybDIuMjkxIDUuOTQ1WiIvPjwvc3ZnPg==",
55
- }
56
-
57
- AGGREGATORS = ["openai://gpt-4o-mini", "openai://gpt-4o"]
58
-
59
 
60
  def anthropic_streamlit_streamer(stream):
61
  """
@@ -88,6 +72,15 @@ def together_streamlit_streamer(stream):
88
  yield chunk.choices[0].delta.content
89
 
90
 
 
 
 
 
 
 
 
 
 
91
  # Helper functions for LLM council and aggregator selection
92
  def llm_council_selector():
93
  selected_council = st.radio(
@@ -133,16 +126,17 @@ def get_google_response(model_name, prompt):
133
  return model.generate_content(prompt, stream=True)
134
 
135
 
136
- def get_llm_response(model_identifier, prompt):
 
137
  provider, model_name = model_identifier.split("://")
138
  if provider == "openai":
139
  return get_openai_response(model_name, prompt)
140
  elif provider == "anthropic":
141
- return get_anthropic_response(model_name, prompt)
142
  elif provider == "together":
143
- return get_together_response(model_name, prompt)
144
  elif provider == "vertex":
145
- return get_google_response(model_name, prompt)
146
  else:
147
  return None
148
 
@@ -151,6 +145,97 @@ def get_response_key(model):
151
  return model + ".response"
152
 
153
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  # Main Streamlit App
155
  def main():
156
  st.set_page_config(
@@ -183,7 +268,7 @@ def main():
183
  # App title and description
184
  st.title("Language Model Council Sandbox")
185
  st.markdown("###### Invoke a council of LLMs to generate and judge each other.")
186
- st.markdown("###### [ArXiv Paper](https://arxiv.org/abs/2406.08598)")
187
 
188
  # Authentication system
189
  if "authenticated" not in st.session_state:
@@ -206,60 +291,151 @@ def main():
206
  selected_models = llm_council_selector()
207
  st.write("Selected Models:", selected_models)
208
  selected_aggregator = aggregator_selector()
209
- st.write("Selected Aggregator:", selected_aggregator)
210
 
211
  # Prompt input
212
- prompt = st.text_area("Enter your prompt:")
213
 
214
  if st.button("Submit"):
215
  st.write("Responses:")
216
 
217
  # Fetching and streaming responses from each selected model
 
218
  for model in selected_models:
219
- # with st.chat_message(model):
220
  with st.chat_message(
221
  model,
222
  avatar=PROVIDER_TO_AVATAR_MAP[model],
223
  ):
224
  message_placeholder = st.empty()
225
- stream = get_llm_response(model, prompt)
226
  if stream:
227
- if model.startswith("anthropic"):
228
- stream = anthropic_streamlit_streamer(stream)
229
- elif model.startswith("vertex"):
230
- stream = google_streamlit_streamer(stream)
231
- elif model.startswith("together"):
232
- stream = together_streamlit_streamer(stream)
233
  st.session_state[get_response_key(model)] = (
234
  message_placeholder.write_stream(stream)
235
  )
236
 
237
- # Constructing the aggregator prompt
238
- aggregator_prompt = f"User prompt: {prompt}\n\n"
239
- aggregator_prompt += "Responses from other LLMs:\n\n"
240
- aggregator_prompt += "\n".join(
241
- [
242
- f"{model}: {st.session_state.get(get_response_key(model))} \n\n"
243
- for model in selected_models
244
- ]
245
  )
246
- aggregator_prompt += "\n\nPlease provide an aggregated response."
247
 
248
  with st.expander("Aggregator Prompt"):
249
  st.write(aggregator_prompt)
250
 
251
  # Fetching and streaming response from the aggregator
252
- st.write(f"Aggregated response from {selected_aggregator}:")
253
  with st.chat_message(
254
  selected_aggregator,
255
  avatar=PROVIDER_TO_AVATAR_MAP[selected_aggregator],
256
  ):
257
  message_placeholder = st.empty()
258
- aggregator_stream = get_llm_response(
259
  selected_aggregator, aggregator_prompt
260
  )
261
  if aggregator_stream:
262
  message_placeholder.write_stream(aggregator_stream)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
263
  else:
264
  with cols[1]:
265
  st.warning("Please log in to access this app.")
@@ -267,72 +443,3 @@ def main():
267
 
268
  if __name__ == "__main__":
269
  main()
270
-
271
-
272
- # Fix the aggregator step.
273
- # Add a judging step.
274
- # Add visualizations.
275
-
276
-
277
- # import streamlit as st
278
- # from components import llm_council_selector
279
-
280
- # st.title("LLM Council Selector")
281
-
282
- # selected_models = llm_council_selector()
283
-
284
- # if selected_models is not None:
285
- # st.write("Selected Models:", selected_models)
286
- # else:
287
- # st.write("No models selected or component didn't return a value.")
288
-
289
-
290
- # Choose your council.
291
- # Pre-selected.
292
- # Smalls: GPT-4o-mini, llama-3.1-70b, qwen-2.0-70b
293
- # Flagships: GPT-4o, llama-3.1-405b, qwen-2.0-110b, gemini, claude-3.5-sonnet
294
- # Best: chatgpt-4o-latest, gemini-1.5-pro-exp-0827, grok-2-2024-08-13, claude-3-5-sonnet-20240620, llama-3.1-405b-instruct
295
- # Custom:
296
- # Choose from a list of available models.
297
- # All:
298
- # All available models.
299
-
300
- # Choose aggregator.
301
- # Aggregators are models proficient in synthesizing responses from other models into a single, highquality output. An effective aggregator should maintain or enhance output quality even when
302
- # integrating inputs that are of lesser quality than its own.
303
- # Choices:
304
- # - 4o-latest
305
- # - gemini-1.5
306
- # - grok-2
307
- # - claude-3.5-sonnet
308
- # - llama-3.1-405b-instruct
309
-
310
- # Provide a prompt. (Or pre-canned prompts.)
311
- # Paste chat history.
312
-
313
- # Checkbox, enable judging.
314
- #
315
- # If checked, Judging config:
316
- # Single sided
317
- # Provide criteria. (or default).
318
- # If pairwise, choose granularity (or default).
319
- # Choose criteria. (or default).
320
- # Enable position swapping?
321
-
322
- # Go button.
323
- # Sections.
324
- # 1. Model outputs.
325
- # 2. Aggregated output.
326
- # 3. Judging underneath each output.
327
- # Highlight in green, the output that was best, as determined by council.
328
- # Show graph breakdown of scores and justifications. (by criteria, # wins and # losses)
329
- # Show final overall score.
330
- # Highlight in red, the output that was worst, as determined by council.
331
- # Judging section.
332
- # Show agreement matrix.
333
- # Show bar graph of self-bias.
334
- # Plot contrarianism vs. conviction (scatter plot)
335
- # Show per-judge scores.
336
-
337
- # Calculate total cost.
338
- # Show total tokens used.
 
7
  from together import Together
8
  import google.generativeai as genai
9
  import time
10
+ from typing import List, Optional, Literal, Union
11
+ from constants import (
12
+ LLM_COUNCIL_MEMBERS,
13
+ PROVIDER_TO_AVATAR_MAP,
14
+ AGGREGATORS,
15
+ )
16
+ from prompts import *
17
+ from judging_dataclasses import *
18
+
19
 
20
  dotenv.load_dotenv()
21
 
 
40
  # anthropic_client = anthropic.Client(api_key=ANTHROPIC_API_KEY)
41
  anthropic_client = anthropic.Anthropic()
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
  def anthropic_streamlit_streamer(stream):
45
  """
 
72
  yield chunk.choices[0].delta.content
73
 
74
 
75
+ def llm_streamlit_streamer(stream, llm):
76
+ if llm.startswith("anthropic"):
77
+ return anthropic_streamlit_streamer(stream)
78
+ elif llm.startswith("vertex"):
79
+ return google_streamlit_streamer(stream)
80
+ elif llm.startswith("together"):
81
+ return together_streamlit_streamer(stream)
82
+
83
+
84
  # Helper functions for LLM council and aggregator selection
85
  def llm_council_selector():
86
  selected_council = st.radio(
 
126
  return model.generate_content(prompt, stream=True)
127
 
128
 
129
+ def get_llm_response_stream(model_identifier, prompt):
130
+ """Returns a streamlit-friendly stream of response tokens from the LLM."""
131
  provider, model_name = model_identifier.split("://")
132
  if provider == "openai":
133
  return get_openai_response(model_name, prompt)
134
  elif provider == "anthropic":
135
+ return anthropic_streamlit_streamer(get_anthropic_response(model_name, prompt))
136
  elif provider == "together":
137
+ return together_streamlit_streamer(get_together_response(model_name, prompt))
138
  elif provider == "vertex":
139
+ return google_streamlit_streamer(get_google_response(model_name, prompt))
140
  else:
141
  return None
142
 
 
145
  return model + ".response"
146
 
147
 
148
+ def get_model_from_response_key(response_key):
149
+ return response_key.split(".")[0]
150
+
151
+
152
+ def get_judging_key(judge_model, response_model):
153
+ return "judge." + judge_model + "." + response_model
154
+
155
+
156
+ def get_aggregator_response_key(model):
157
+ return model + ".aggregator_response"
158
+
159
+
160
+ # Streamlit form UI
161
+ def render_criteria_form(criteria_num):
162
+ """Render a criteria input form."""
163
+ with st.expander(f"Criteria {criteria_num + 1}"):
164
+ name = st.text_input(f"Name for Criteria {criteria_num + 1}")
165
+ description = st.text_area(f"Description for Criteria {criteria_num + 1}")
166
+ min_score = st.number_input(
167
+ f"Min Score for Criteria {criteria_num + 1}", min_value=0, step=1
168
+ )
169
+ max_score = st.number_input(
170
+ f"Max Score for Criteria {criteria_num + 1}", min_value=0, step=1
171
+ )
172
+ return Criteria(
173
+ name=name, description=description, min_score=min_score, max_score=max_score
174
+ )
175
+
176
+
177
+ def get_response_mapping():
178
+ # Inspect the session state for all the responses.
179
+ # This is a dictionary mapping model names to their responses.
180
+ # The aggregator response is also included in this mapping under the key "<model>.aggregator_response".
181
+ response_mapping = {}
182
+ for key in st.session_state.keys():
183
+ if key.endswith(".response"):
184
+ response_mapping[get_model_from_response_key(key)] = st.session_state[key]
185
+ if key.endswith(".aggregator_response"):
186
+ response_mapping[key] = st.session_state[key]
187
+ return response_mapping
188
+
189
+
190
+ def format_likert_comparison_options(options):
191
+ return "\n".join([f"{i + 1}: {option}" for i, option in enumerate(options)])
192
+
193
+
194
+ def format_criteria_list(criteria_list):
195
+ return "\n".join(
196
+ [f"{criteria.name}: {criteria.description}" for criteria in criteria_list]
197
+ )
198
+
199
+
200
+ def get_direct_assessment_prompt(
201
+ direct_assessment_prompt, user_prompt, response, criteria_list, options
202
+ ):
203
+ return direct_assessment_prompt.format(
204
+ user_prompt=user_prompt,
205
+ response=response,
206
+ criteria_list=f"{format_criteria_list(DEFAULT_DIRECT_ASSESSMENT_CRITERIA_LIST)}",
207
+ options=f"{format_likert_comparison_options(SEVEN_POINT_DIRECT_ASSESSMENT_OPTIONS)}",
208
+ )
209
+
210
+
211
+ def get_default_direct_assessment_prompt(user_prompt):
212
+ return get_direct_assessment_prompt(
213
+ DEFAULT_DIRECT_ASSESSMENT_PROMPT,
214
+ user_prompt=user_prompt,
215
+ response="{{response}}",
216
+ criteria_list=DEFAULT_DIRECT_ASSESSMENT_CRITERIA_LIST,
217
+ options=SEVEN_POINT_DIRECT_ASSESSMENT_OPTIONS,
218
+ )
219
+
220
+
221
+ def get_aggregator_prompt(aggregator_prompt, user_prompt, llms):
222
+ responses_from_other_llms = "\n\n".join(
223
+ [f"{model}: {st.session_state.get(get_response_key(model))}" for model in llms]
224
+ )
225
+ return aggregator_prompt.format(
226
+ user_prompt=user_prompt,
227
+ responses_from_other_llms=responses_from_other_llms,
228
+ )
229
+
230
+
231
+ def get_default_aggregator_prompt(user_prompt, llms):
232
+ return get_aggregator_prompt(
233
+ DEFAULT_AGGREGATOR_PROMPT,
234
+ user_prompt=user_prompt,
235
+ llms=llms,
236
+ )
237
+
238
+
239
  # Main Streamlit App
240
  def main():
241
  st.set_page_config(
 
268
  # App title and description
269
  st.title("Language Model Council Sandbox")
270
  st.markdown("###### Invoke a council of LLMs to generate and judge each other.")
271
+ st.markdown("###### [Paper](https://arxiv.org/abs/2406.08598)")
272
 
273
  # Authentication system
274
  if "authenticated" not in st.session_state:
 
291
  selected_models = llm_council_selector()
292
  st.write("Selected Models:", selected_models)
293
  selected_aggregator = aggregator_selector()
294
+ # st.write("Selected Aggregator:", selected_aggregator)
295
 
296
  # Prompt input
297
+ user_prompt = st.text_area("Enter your prompt:")
298
 
299
  if st.button("Submit"):
300
  st.write("Responses:")
301
 
302
  # Fetching and streaming responses from each selected model
303
+ # TODO: Make this asynchronous?
304
  for model in selected_models:
 
305
  with st.chat_message(
306
  model,
307
  avatar=PROVIDER_TO_AVATAR_MAP[model],
308
  ):
309
  message_placeholder = st.empty()
310
+ stream = get_llm_response_stream(model, user_prompt)
311
  if stream:
 
 
 
 
 
 
312
  st.session_state[get_response_key(model)] = (
313
  message_placeholder.write_stream(stream)
314
  )
315
 
316
+ # Get the aggregator prompt.
317
+ aggregator_prompt = get_default_aggregator_prompt(
318
+ user_prompt=user_prompt, llms=selected_models
 
 
 
 
 
319
  )
 
320
 
321
  with st.expander("Aggregator Prompt"):
322
  st.write(aggregator_prompt)
323
 
324
  # Fetching and streaming response from the aggregator
325
+ st.write(f"Mixture-of-Agents response from {selected_aggregator}:")
326
  with st.chat_message(
327
  selected_aggregator,
328
  avatar=PROVIDER_TO_AVATAR_MAP[selected_aggregator],
329
  ):
330
  message_placeholder = st.empty()
331
+ aggregator_stream = get_llm_response_stream(
332
  selected_aggregator, aggregator_prompt
333
  )
334
  if aggregator_stream:
335
  message_placeholder.write_stream(aggregator_stream)
336
+ st.session_state[
337
+ get_aggregator_response_key(selected_aggregator)
338
+ ] = message_placeholder.write_stream(aggregator_stream)
339
+
340
+ # Judging.
341
+ st.markdown("#### Judging Configuration Form")
342
+
343
+ # Choose the type of assessment
344
+ assessment_type = st.radio(
345
+ "Select the type of assessment",
346
+ options=["Direct Assessment", "Pairwise Comparison"],
347
+ )
348
+
349
+ # Depending on the assessment type, render different forms
350
+ if assessment_type == "Direct Assessment":
351
+ direct_assessment_prompt = st.text_area(
352
+ "Prompt for the Direct Assessment",
353
+ value=get_default_direct_assessment_prompt(user_prompt=user_prompt),
354
+ height=500,
355
+ )
356
+
357
+ # TODO: Add option to edit criteria list with a basic text field.
358
+ criteria_list = DEFAULT_DIRECT_ASSESSMENT_CRITERIA_LIST
359
+
360
+ # Create DirectAssessment object when form is submitted
361
+ if st.button("Submit Direct Assessment"):
362
+
363
+ # Submit direct asssessment.
364
+ responses_for_judging = get_response_mapping()
365
+
366
+ response_judging_columns = st.columns(3)
367
+
368
+ responses_for_judging_to_streamlit_column_index_map = {
369
+ model: response_judging_columns[i % 3]
370
+ for i, model in enumerate(responses_for_judging.keys())
371
+ }
372
+
373
+ # Get judging responses.
374
+ for response_model, response in responses_for_judging.items():
375
+
376
+ st_column = response_judging_columns[
377
+ responses_for_judging_to_streamlit_column_index_map[
378
+ response_model
379
+ ]
380
+ ]
381
+
382
+ with st_column:
383
+
384
+ st.write(f"Judging {response_model}")
385
+ judging_prompt = get_direct_assessment_prompt(
386
+ direct_assessment_prompt,
387
+ user_prompt,
388
+ response,
389
+ criteria_list,
390
+ SEVEN_POINT_DIRECT_ASSESSMENT_OPTIONS,
391
+ )
392
+
393
+ for judging_model in selected_models:
394
+ with st.expander("Detailed assessments", expanded=True):
395
+ with st.chat_message(
396
+ judging_model,
397
+ avatar=PROVIDER_TO_AVATAR_MAP[judging_model],
398
+ ):
399
+ st.write(f"Judge: {judging_model}")
400
+ message_placeholder = st.empty()
401
+ judging_stream = get_llm_response_stream(
402
+ judging_model, judging_prompt
403
+ )
404
+ if judging_stream:
405
+ st.session_state[
406
+ get_judging_key(
407
+ judging_model, response_model
408
+ )
409
+ ] = message_placeholder.write_stream(
410
+ judging_stream
411
+ )
412
+ # When all of the judging is finished for the given response, get the actual
413
+ # values, parsed (use gpt-4o-mini for now) with json mode.
414
+ # TODO.
415
+
416
+ elif assessment_type == "Pairwise Comparison":
417
+ pairwise_comparison_prompt = st.text_area(
418
+ "Prompt for the Pairwise Comparison"
419
+ )
420
+ granularity = st.selectbox("Granularity", ["coarse", "fine", "super fine"])
421
+ ties_allowed = st.checkbox("Are ties allowed?")
422
+ position_swapping = st.checkbox("Enable position swapping?")
423
+ reference_model = st.text_input("Reference Model")
424
+
425
+ # Create PairwiseComparison object when form is submitted
426
+ if st.button("Submit Pairwise Comparison"):
427
+ pairwise_comparison_config = PairwiseComparison(
428
+ type="pairwise_comparison",
429
+ granularity=granularity,
430
+ ties_allowed=ties_allowed,
431
+ position_swapping=position_swapping,
432
+ reference_model=reference_model,
433
+ prompt=prompt,
434
+ )
435
+ st.success(f"Pairwise Comparison Created: {pairwise_comparison_config}")
436
+ # Submit pairwise comparison.
437
+ responses_for_judging = get_response_mapping()
438
+
439
  else:
440
  with cols[1]:
441
  st.warning("Please log in to access this app.")
 
443
 
444
  if __name__ == "__main__":
445
  main()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app2.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import google_auth_oauthlib.flow
4
+ from googleapiclient.discovery import build
5
+ import streamlit as st
6
+ import webbrowser
7
+
8
+
9
+ redirect_uri = os.environ.get("REDIRECT_URI", "http://localhost:8502/")
10
+
11
+
12
+ def auth_flow():
13
+ st.write("Welcome to My App!")
14
+ auth_code = st.query_params.get("code")
15
+ flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
16
+ "client_secret.json", # replace with you json credentials from your google auth app
17
+ scopes=["https://www.googleapis.com/auth/userinfo.email", "openid"],
18
+ redirect_uri=redirect_uri,
19
+ )
20
+ if auth_code:
21
+ flow.fetch_token(code=auth_code)
22
+ credentials = flow.credentials
23
+ st.write("Login Done")
24
+ user_info_service = build(
25
+ serviceName="oauth2",
26
+ version="v2",
27
+ credentials=credentials,
28
+ )
29
+ user_info = user_info_service.userinfo().get().execute()
30
+ assert user_info.get("email"), "Email not found in infos"
31
+ st.session_state["google_auth_code"] = auth_code
32
+ st.session_state["user_info"] = user_info
33
+ else:
34
+ if st.button("Sign in with Google"):
35
+ authorization_url, state = flow.authorization_url(
36
+ access_type="offline",
37
+ include_granted_scopes="true",
38
+ )
39
+ webbrowser.open_new_tab(authorization_url)
40
+
41
+
42
+ def main():
43
+ if "google_auth_code" not in st.session_state:
44
+ auth_flow()
45
+
46
+ if "google_auth_code" in st.session_state:
47
+ email = st.session_state["user_info"].get("email")
48
+ st.write(f"Hello {email}")
49
+
50
+
51
+ if __name__ == "__main__":
52
+ main()
constants.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ LLM_COUNCIL_MEMBERS = {
2
+ "Smalls": [
3
+ # "openai://gpt-4o-mini",
4
+ "together://meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo",
5
+ "together://meta-llama/Llama-3.2-3B-Instruct-Turbo",
6
+ # "vertex://gemini-1.5-flash-001",
7
+ # "anthropic://claude-3-haiku-20240307",
8
+ ],
9
+ "Flagships": [
10
+ "openai://gpt-4o",
11
+ "together://meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo",
12
+ "vertex://gemini-1.5-pro-001",
13
+ "anthropic://claude-3-5-sonnet",
14
+ ],
15
+ }
16
+
17
+ PROVIDER_TO_AVATAR_MAP = {
18
+ "openai://gpt-4o-mini": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIwLjk5ZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjU2IDI2MCI+PHBhdGggZD0iTTIzOS4xODQgMTA2LjIwM2E2NC43MiA2NC43MiAwIDAgMC01LjU3Ni01My4xMDNDMjE5LjQ1MiAyOC40NTkgMTkxIDE1Ljc4NCAxNjMuMjEzIDIxLjc0QTY1LjU4NiA2NS41ODYgMCAwIDAgNTIuMDk2IDQ1LjIyYTY0LjcyIDY0LjcyIDAgMCAwLTQzLjIzIDMxLjM2Yy0xNC4zMSAyNC42MDItMTEuMDYxIDU1LjYzNCA4LjAzMyA3Ni43NGE2NC42NyA2NC42NyAwIDAgMCA1LjUyNSA1My4xMDJjMTQuMTc0IDI0LjY1IDQyLjY0NCAzNy4zMjQgNzAuNDQ2IDMxLjM2YTY0LjcyIDY0LjcyIDAgMCAwIDQ4Ljc1NCAyMS43NDRjMjguNDgxLjAyNSA1My43MTQtMTguMzYxIDYyLjQxNC00NS40ODFhNjQuNzcgNjQuNzcgMCAwIDAgNDMuMjI5LTMxLjM2YzE0LjEzNy0yNC41NTggMTAuODc1LTU1LjQyMy04LjA4My03Ni40ODNtLTk3LjU2IDEzNi4zMzhhNDguNCA0OC40IDAgMCAxLTMxLjEwNS0xMS4yNTVsMS41MzUtLjg3bDUxLjY3LTI5LjgyNWE4LjYgOC42IDAgMCAwIDQuMjQ3LTcuMzY3di03Mi44NWwyMS44NDUgMTIuNjM2Yy4yMTguMTExLjM3LjMyLjQwOS41NjN2NjAuMzY3Yy0uMDU2IDI2LjgxOC0yMS43ODMgNDguNTQ1LTQ4LjYwMSA0OC42MDFNMzcuMTU4IDE5Ny45M2E0OC4zNSA0OC4zNSAwIDAgMS01Ljc4MS0zMi41ODlsMS41MzQuOTIxbDUxLjcyMiAyOS44MjZhOC4zNCA4LjM0IDAgMCAwIDguNDQxIDBsNjMuMTgxLTM2LjQyNXYyNS4yMjFhLjg3Ljg3IDAgMCAxLS4zNTguNjY1bC01Mi4zMzUgMzAuMTg0Yy0yMy4yNTcgMTMuMzk4LTUyLjk3IDUuNDMxLTY2LjQwNC0xNy44MDNNMjMuNTQ5IDg1LjM4YTQ4LjUgNDguNSAwIDAgMSAyNS41OC0yMS4zMzN2NjEuMzlhOC4yOSA4LjI5IDAgMCAwIDQuMTk1IDcuMzE2bDYyLjg3NCAzNi4yNzJsLTIxLjg0NSAxMi42MzZhLjgyLjgyIDAgMCAxLS43NjcgMEw0MS4zNTMgMTUxLjUzYy0yMy4yMTEtMTMuNDU0LTMxLjE3MS00My4xNDQtMTcuODA0LTY2LjQwNXptMTc5LjQ2NiA0MS42OTVsLTYzLjA4LTM2LjYzTDE2MS43MyA3Ny44NmEuODIuODIgMCAwIDEgLjc2OCAwbDUyLjIzMyAzMC4xODRhNDguNiA0OC42IDAgMCAxLTcuMzE2IDg3LjYzNXYtNjEuMzkxYTguNTQgOC41NCAwIDAgMC00LjQtNy4yMTNtMjEuNzQyLTMyLjY5bC0xLjUzNS0uOTIybC01MS42MTktMzAuMDgxYTguMzkgOC4zOSAwIDAgMC04LjQ5MiAwTDk5Ljk4IDk5LjgwOFY3NC41ODdhLjcyLjcyIDAgMCAxIC4zMDctLjY2NWw1Mi4yMzMtMzAuMTMzYTQ4LjY1MiA0OC42NTIgMCAwIDEgNzIuMjM2IDUwLjM5MXpNODguMDYxIDEzOS4wOTdsLTIxLjg0NS0xMi41ODVhLjg3Ljg3IDAgMCAxLS40MS0uNjE0VjY1LjY4NWE0OC42NTIgNDguNjUyIDAgMCAxIDc5Ljc1Ny0zNy4zNDZsLTEuNTM1Ljg3bC01MS42NyAyOS44MjVhOC42IDguNiAwIDAgMC00LjI0NiA3LjM2N3ptMTEuODY4LTI1LjU4TDEyOC4wNjcgOTcuM2wyOC4xODggMTYuMjE4djMyLjQzNGwtMjguMDg2IDE2LjIxOGwtMjguMTg4LTE2LjIxOHoiLz48L3N2Zz4=",
19
+ "anthropic://claude-3-5-sonnet": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTE3LjMwNCAzLjU0MWgtMy42NzJsNi42OTYgMTYuOTE4SDI0Wm0tMTAuNjA4IDBMMCAyMC40NTloMy43NDRsMS4zNy0zLjU1M2g3LjAwNWwxLjM2OSAzLjU1M2gzLjc0NEwxMC41MzYgMy41NDFabS0uMzcxIDEwLjIyM0w4LjYxNiA3LjgybDIuMjkxIDUuOTQ1WiIvPjwvc3ZnPg==",
20
+ "vertex://gemini-1.5-flash-001": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9IiM0MjY4ZmYiIGQ9Ik0yNCAxMi4wMjRjLTYuNDM3LjM4OC0xMS41OSA1LjUzOS0xMS45NzcgMTEuOTc2aC0uMDQ3QzExLjU4OCAxNy41NjMgNi40MzYgMTIuNDEyIDAgMTIuMDI0di0uMDQ3QzYuNDM3IDExLjU4OCAxMS41ODggNi40MzcgMTEuOTc2IDBoLjA0N2MuMzg4IDYuNDM3IDUuNTQgMTEuNTg4IDExLjk3NyAxMS45Nzd6Ii8+PC9zdmc+",
21
+ "together://meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGZpbGw9IiNiNGFjYmMiIGQ9Ik0yMC44NzEgMjQuNzh2LTYuMDZoMy4wMXY1Ljc3YzAgLjM0LS4xMi42Mi0uMzEuOTRsLTIuNDEgNC4yYy0uMTguMjMtLjQ1LjM3LS43NS4zN2gtMS4wM2MtLjIzIDAtLjM4LS4yNC0uMjgtLjQ1bDEuNjctNC4zNWMuMDctLjEzLjEtLjI3LjEtLjQyTTE3LjA5MSAzMGMuMiAwIC4yNi0uMjEuMjItLjM4bC0yLjMyLTguNjFoLTIuOTlsLjg1IDMuNTVjLjE5LjcxLjY2IDEuMzIgMS4zIDEuNjljLjE0LjA4LjI1LjIyLjI5LjM4bC44NyAzLjE0Yy4wNC4xNy4yMS4yMi4zOC4yMnoiLz48cGF0aCBmaWxsPSIjY2RjNGQ2IiBkPSJtMjguNTQxIDIzLjA4bC0xLjI3LS45NmEuOTQuOTQgMCAwIDEtLjI3LS42NnYtMi43NWMwLS42NC0uMTYtMS4yMzgtLjQ0Mi0xLjc2cS4yMTMuMDUuNDQyLjA1YTIgMiAwIDEgMC0xLjk0OS0xLjU0OWEzLjggMy44IDAgMCAwLTEuOC0uNDUxaC04LjE3Yy0uNjYgMC0xLjI3LS40Mi0xLjU3LTEuMDFMMTAuMDQxIDMuNWEyLjIzIDIuMjMgMCAwIDAtMi4xLTEuNWMtLjE4IDAtLjMuMTctLjI0LjM0TDguNTcxIDVjLS4yIDAtMS4wNy4yMy0xLjg1LjczbC0uODA2LjQ5OEw3LjAwMiAxMHY4LjI2YzAgMi4wMSAxLjI1IDMuNzIgMy4wMSA0LjQxdjcuMDJjLS4wMS4xNy4xMy4zMS4zLjMxaDEuMzdjLjE4IDAgLjMyLS4xNC4zMi0uMzF2LTEuOTZjMC0uMTcuMDctLjMyLjE4LS40NGMuNTMtLjUyLjgyLTEuMjMuODItMS45N1YyM2g1LjA3YzEuMjcgMCAyLjQ5LS41NSAzLjMzLTEuNWMwIC45NC40OCAxLjcyIDEuMzggMi4zMmwzLjk2IDIuNDNjLjE2LjExLjI2LjMuMjYuNXYyLjkzYzAgLjE3LjE0LjMxLjMxLjMxaDEuMzdjLjE3IDAgLjMxLS4xNC4zMS0uMzF2LTUuNTFjLjAxLS40LS4xNS0uOC0uNDUtMS4wOSIvPjxwYXRoIGZpbGw9IiNmM2FkNjEiIGQ9Ik02Ljg0MSA2Yy0uMzYgMC0uNzIuMS0xLjAzLjI5bC0yLjE5IDEuMzVjLS4zNi4yMy0uNi42MS0uNjIgMS4wM2MtLjAzLjczLjU1IDEuMzMgMS4yNyAxLjMzaDMuNDljLjU3IDAgMS4wNC0uNDcgMS4wNC0xLjA1di0xYzAtMS4wNy0uODgtMS45NS0xLjk2LTEuOTUiLz48cGF0aCBmaWxsPSIjMWMxYzFjIiBkPSJNNi41IDhhLjUuNSAwIDEgMCAwLTFhLjUuNSAwIDAgMCAwIDFtLTEuOTk5LjVjMC0uMjgtLjIyLS41LS41LS41aC0uNzZhMS4yIDEuMiAwIDAgMC0uMjEgMWguOTdjLjI4IDAgLjUtLjIyLjUtLjUiLz48cGF0aCBmaWxsPSIjZjNhZDYxIiBkPSJNMjguOTkxIDI4aC0xLjk5djEuNjhjMCAuMTcuMTQuMzEuMzEuMzFoMS4zN2MuMTcgMCAuMzEtLjE0LjMxLS4zMXptLTE2Ljk5IDBoLTEuOTl2MS42OWMtLjAxLjE3LjEzLjMxLjMuMzFoMS4zN2MuMTggMCAuMzItLjE0LjMyLS4zMXptNS4wODggMmwtMS4zOTgtLjAxYy0uMTcgMC0uMzQtLjA1LS4zOC0uMjJsLS40OS0xLjc3aDIuMDU0bC40MzYgMS42MmMuMDQuMTctLjAyLjM3OC0uMjE2LjM4em0yLjI4OCAwYS4zMTMuMzEzIDAgMCAxLS4yNzYtLjQ1bC41OTUtMS41NWgyLjRsLS45MzUgMS42M2EuOTUuOTUgMCAwIDEtLjc0Mi4zN3oiLz48L2c+PC9zdmc+",
22
+ "together://meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGZpbGw9IiNiNGFjYmMiIGQ9Ik0yMC44NzEgMjQuNzh2LTYuMDZoMy4wMXY1Ljc3YzAgLjM0LS4xMi42Mi0uMzEuOTRsLTIuNDEgNC4yYy0uMTguMjMtLjQ1LjM3LS43NS4zN2gtMS4wM2MtLjIzIDAtLjM4LS4yNC0uMjgtLjQ1bDEuNjctNC4zNWMuMDctLjEzLjEtLjI3LjEtLjQyTTE3LjA5MSAzMGMuMiAwIC4yNi0uMjEuMjItLjM4bC0yLjMyLTguNjFoLTIuOTlsLjg1IDMuNTVjLjE5LjcxLjY2IDEuMzIgMS4zIDEuNjljLjE0LjA4LjI1LjIyLjI5LjM4bC44NyAzLjE0Yy4wNC4xNy4yMS4yMi4zOC4yMnoiLz48cGF0aCBmaWxsPSIjY2RjNGQ2IiBkPSJtMjguNTQxIDIzLjA4bC0xLjI3LS45NmEuOTQuOTQgMCAwIDEtLjI3LS42NnYtMi43NWMwLS42NC0uMTYtMS4yMzgtLjQ0Mi0xLjc2cS4yMTMuMDUuNDQyLjA1YTIgMiAwIDEgMC0xLjk0OS0xLjU0OWEzLjggMy44IDAgMCAwLTEuOC0uNDUxaC04LjE3Yy0uNjYgMC0xLjI3LS40Mi0xLjU3LTEuMDFMMTAuMDQxIDMuNWEyLjIzIDIuMjMgMCAwIDAtMi4xLTEuNWMtLjE4IDAtLjMuMTctLjI0LjM0TDguNTcxIDVjLS4yIDAtMS4wNy4yMy0xLjg1LjczbC0uODA2LjQ5OEw3LjAwMiAxMHY4LjI2YzAgMi4wMSAxLjI1IDMuNzIgMy4wMSA0LjQxdjcuMDJjLS4wMS4xNy4xMy4zMS4zLjMxaDEuMzdjLjE4IDAgLjMyLS4xNC4zMi0uMzF2LTEuOTZjMC0uMTcuMDctLjMyLjE4LS40NGMuNTMtLjUyLjgyLTEuMjMuODItMS45N1YyM2g1LjA3YzEuMjcgMCAyLjQ5LS41NSAzLjMzLTEuNWMwIC45NC40OCAxLjcyIDEuMzggMi4zMmwzLjk2IDIuNDNjLjE2LjExLjI2LjMuMjYuNXYyLjkzYzAgLjE3LjE0LjMxLjMxLjMxaDEuMzdjLjE3IDAgLjMxLS4xNC4zMS0uMzF2LTUuNTFjLjAxLS40LS4xNS0uOC0uNDUtMS4wOSIvPjxwYXRoIGZpbGw9IiNmM2FkNjEiIGQ9Ik02Ljg0MSA2Yy0uMzYgMC0uNzIuMS0xLjAzLjI5bC0yLjE5IDEuMzVjLS4zNi4yMy0uNi42MS0uNjIgMS4wM2MtLjAzLjczLjU1IDEuMzMgMS4yNyAxLjMzaDMuNDljLjU3IDAgMS4wNC0uNDcgMS4wNC0xLjA1di0xYzAtMS4wNy0uODgtMS45NS0xLjk2LTEuOTUiLz48cGF0aCBmaWxsPSIjMWMxYzFjIiBkPSJNNi41IDhhLjUuNSAwIDEgMCAwLTFhLjUuNSAwIDAgMCAwIDFtLTEuOTk5LjVjMC0uMjgtLjIyLS41LS41LS41aC0uNzZhMS4yIDEuMiAwIDAgMC0uMjEgMWguOTdjLjI4IDAgLjUtLjIyLjUtLjUiLz48cGF0aCBmaWxsPSIjZjNhZDYxIiBkPSJNMjguOTkxIDI4aC0xLjk5djEuNjhjMCAuMTcuMTQuMzEuMzEuMzFoMS4zN2MuMTcgMCAuMzEtLjE0LjMxLS4zMXptLTE2Ljk5IDBoLTEuOTl2MS42OWMtLjAxLjE3LjEzLjMxLjMuMzFoMS4zN2MuMTggMCAuMzItLjE0LjMyLS4zMXptNS4wODggMmwtMS4zOTgtLjAxYy0uMTcgMC0uMzQtLjA1LS4zOC0uMjJsLS40OS0xLjc3aDIuMDU0bC40MzYgMS42MmMuMDQuMTctLjAyLjM3OC0uMjE2LjM4em0yLjI4OCAwYS4zMTMuMzEzIDAgMCAxLS4yNzYtLjQ1bC41OTUtMS41NWgyLjRsLS45MzUgMS42M2EuOTUuOTUgMCAwIDEtLjc0Mi4zN3oiLz48L2c+PC9zdmc+",
23
+ "together://meta-llama/Llama-3.2-3B-Instruct-Turbo": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxnIGZpbGw9Im5vbmUiPjxwYXRoIGZpbGw9IiNiNGFjYmMiIGQ9Ik0yMC44NzEgMjQuNzh2LTYuMDZoMy4wMXY1Ljc3YzAgLjM0LS4xMi42Mi0uMzEuOTRsLTIuNDEgNC4yYy0uMTguMjMtLjQ1LjM3LS43NS4zN2gtMS4wM2MtLjIzIDAtLjM4LS4yNC0uMjgtLjQ1bDEuNjctNC4zNWMuMDctLjEzLjEtLjI3LjEtLjQyTTE3LjA5MSAzMGMuMiAwIC4yNi0uMjEuMjItLjM4bC0yLjMyLTguNjFoLTIuOTlsLjg1IDMuNTVjLjE5LjcxLjY2IDEuMzIgMS4zIDEuNjljLjE0LjA4LjI1LjIyLjI5LjM4bC44NyAzLjE0Yy4wNC4xNy4yMS4yMi4zOC4yMnoiLz48cGF0aCBmaWxsPSIjY2RjNGQ2IiBkPSJtMjguNTQxIDIzLjA4bC0xLjI3LS45NmEuOTQuOTQgMCAwIDEtLjI3LS42NnYtMi43NWMwLS42NC0uMTYtMS4yMzgtLjQ0Mi0xLjc2cS4yMTMuMDUuNDQyLjA1YTIgMiAwIDEgMC0xLjk0OS0xLjU0OWEzLjggMy44IDAgMCAwLTEuOC0uNDUxaC04LjE3Yy0uNjYgMC0xLjI3LS40Mi0xLjU3LTEuMDFMMTAuMDQxIDMuNWEyLjIzIDIuMjMgMCAwIDAtMi4xLTEuNWMtLjE4IDAtLjMuMTctLjI0LjM0TDguNTcxIDVjLS4yIDAtMS4wNy4yMy0xLjg1LjczbC0uODA2LjQ5OEw3LjAwMiAxMHY4LjI2YzAgMi4wMSAxLjI1IDMuNzIgMy4wMSA0LjQxdjcuMDJjLS4wMS4xNy4xMy4zMS4zLjMxaDEuMzdjLjE4IDAgLjMyLS4xNC4zMi0uMzF2LTEuOTZjMC0uMTcuMDctLjMyLjE4LS40NGMuNTMtLjUyLjgyLTEuMjMuODItMS45N1YyM2g1LjA3YzEuMjcgMCAyLjQ5LS41NSAzLjMzLTEuNWMwIC45NC40OCAxLjcyIDEuMzggMi4zMmwzLjk2IDIuNDNjLjE2LjExLjI2LjMuMjYuNXYyLjkzYzAgLjE3LjE0LjMxLjMxLjMxaDEuMzdjLjE3IDAgLjMxLS4xNC4zMS0uMzF2LTUuNTFjLjAxLS40LS4xNS0uOC0uNDUtMS4wOSIvPjxwYXRoIGZpbGw9IiNmM2FkNjEiIGQ9Ik02Ljg0MSA2Yy0uMzYgMC0uNzIuMS0xLjAzLjI5bC0yLjE5IDEuMzVjLS4zNi4yMy0uNi42MS0uNjIgMS4wM2MtLjAzLjczLjU1IDEuMzMgMS4yNyAxLjMzaDMuNDljLjU3IDAgMS4wNC0uNDcgMS4wNC0xLjA1di0xYzAtMS4wNy0uODgtMS45NS0xLjk2LTEuOTUiLz48cGF0aCBmaWxsPSIjMWMxYzFjIiBkPSJNNi41IDhhLjUuNSAwIDEgMCAwLTFhLjUuNSAwIDAgMCAwIDFtLTEuOTk5LjVjMC0uMjgtLjIyLS41LS41LS41aC0uNzZhMS4yIDEuMiAwIDAgMC0uMjEgMWguOTdjLjI4IDAgLjUtLjIyLjUtLjUiLz48cGF0aCBmaWxsPSIjZjNhZDYxIiBkPSJNMjguOTkxIDI4aC0xLjk5djEuNjhjMCAuMTcuMTQuMzEuMzEuMzFoMS4zN2MuMTcgMCAuMzEtLjE0LjMxLS4zMXptLTE2Ljk5IDBoLTEuOTl2MS42OWMtLjAxLjE3LjEzLjMxLjMuMzFoMS4zN2MuMTggMCAuMzItLjE0LjMyLS4zMXptNS4wODggMmwtMS4zOTgtLjAxYy0uMTcgMC0uMzQtLjA1LS4zOC0uMjJsLS40OS0xLjc3aDIuMDU0bC40MzYgMS42MmMuMDQuMTctLjAyLjM3OC0uMjE2LjM4em0yLjI4OCAwYS4zMTMuMzEzIDAgMCAxLS4yNzYtLjQ1bC41OTUtMS41NWgyLjRsLS45MzUgMS42M2EuOTUuOTUgMCAwIDEtLjc0Mi4zN3oiLz48L2c+PC9zdmc+",
24
+ "anthropic://claude-3-haiku-20240307": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGZpbGw9ImN1cnJlbnRDb2xvciIgZD0iTTE3LjMwNCAzLjU0MWgtMy42NzJsNi42OTYgMTYuOTE4SDI0Wm0tMTAuNjA4IDBMMCAyMC40NTloMy43NDRsMS4zNy0zLjU1M2g3LjAwNWwxLjM2OSAzLjU1M2gzLjc0NEwxMC41MzYgMy41NDFabS0uMzcxIDEwLjIyM0w4LjYxNiA3LjgybDIuMjkxIDUuOTQ1WiIvPjwvc3ZnPg==",
25
+ }
26
+
27
+ # AGGREGATORS = ["openai://gpt-4o-mini", "openai://gpt-4o"]
28
+ AGGREGATORS = ["together://meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo"]
29
+
30
+
31
+ # Fix the aggregator step.
32
+ # Add a judging step.
33
+ # Add visualizations.
34
+
35
+
36
+ # import streamlit as st
37
+ # from components import llm_council_selector
38
+
39
+ # st.title("LLM Council Selector")
40
+
41
+ # selected_models = llm_council_selector()
42
+
43
+ # if selected_models is not None:
44
+ # st.write("Selected Models:", selected_models)
45
+ # else:
46
+ # st.write("No models selected or component didn't return a value.")
47
+
48
+
49
+ # Choose your council.
50
+ # Pre-selected.
51
+ # Smalls: GPT-4o-mini, llama-3.1-70b, qwen-2.0-70b
52
+ # Flagships: GPT-4o, llama-3.1-405b, qwen-2.0-110b, gemini, claude-3.5-sonnet
53
+ # Best: chatgpt-4o-latest, gemini-1.5-pro-exp-0827, grok-2-2024-08-13, claude-3-5-sonnet-20240620, llama-3.1-405b-instruct
54
+ # Custom:
55
+ # Choose from a list of available models.
56
+ # All:
57
+ # All available models.
58
+
59
+ # Choose aggregator.
60
+ # Aggregators are models proficient in synthesizing responses from other models into a single, highquality output. An effective aggregator should maintain or enhance output quality even when
61
+ # integrating inputs that are of lesser quality than its own.
62
+ # Choices:
63
+ # - 4o-latest
64
+ # - gemini-1.5
65
+ # - grok-2
66
+ # - claude-3.5-sonnet
67
+ # - llama-3.1-405b-instruct
68
+
69
+ # Provide a prompt. (Or pre-canned prompts.)
70
+ # Paste chat history.
71
+
72
+ # Checkbox, enable judging.
73
+ #
74
+ # If checked, Judging config:
75
+ # Single sided
76
+ # Provide criteria. (or default).
77
+ # If pairwise, choose granularity (or default).
78
+ # Choose criteria. (or default).
79
+ # Enable position swapping?
80
+
81
+ # Go button.
82
+ # Sections.
83
+ # 1. Model outputs.
84
+ # 2. Aggregated output.
85
+ # 3. Judging underneath each output.
86
+ # Highlight in green, the output that was best, as determined by council.
87
+ # Show graph breakdown of scores and justifications. (by criteria, # wins and # losses)
88
+ # Show final overall score.
89
+ # Highlight in red, the output that was worst, as determined by council.
90
+ # Judging section.
91
+ # Show agreement matrix.
92
+ # Show bar graph of self-bias.
93
+ # Plot contrarianism vs. conviction (scatter plot)
94
+ # Show per-judge scores.
95
+
96
+ # Calculate total cost.
97
+ # Show total tokens used.
98
+
99
+ # """
100
+ # type: [single, pairwise]
101
+
102
+ # [single]
103
+ # - criteria:
104
+ # - name
105
+ # - weight
106
+ # - description
107
+ # - scoring
108
+
109
+
110
+ # [pairwise]
111
+ # - granularity: [fine, coarse]
112
+ # - ties_allowed: [yes, no]
113
+ # - position_swapping: [yes, no]
114
+ # - reference_model: [model_name]
115
+ # - criteria:
116
+ # - name
117
+ # - weight
118
+ # - description
119
+ # """
judging.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field, conint
2
+ from typing import List, Optional, Literal, Union
3
+
4
+
5
+ class Criteria(BaseModel):
6
+ name: str
7
+ description: str
8
+ min_score: conint(ge=0)
9
+ max_score: conint(ge=0)
10
+
11
+
12
+ class DirectAssessment(BaseModel):
13
+ type: Literal["direct_assessment"]
14
+ criteria: List[Criteria]
15
+ prompt: str
16
+
17
+
18
+ class PairwiseComparison(BaseModel):
19
+ type: Literal["pairwise_comparison"]
20
+ granularity: Literal["coarse", "fine", "super fine"]
21
+ ties_allowed: bool
22
+ position_swapping: bool
23
+ reference_model: str
24
+ prompt: str
25
+
26
+
27
+ class JudgingConfig(BaseModel):
28
+ assessment: Union[DirectAssessment, PairwiseComparison]
judging_dataclasses.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel, Field, conint
2
+ from typing import List, Optional, Literal, Union
3
+
4
+
5
+ class Criteria(BaseModel):
6
+ name: str
7
+ description: str
8
+ min_score: conint(ge=0)
9
+ max_score: conint(ge=0)
10
+
11
+
12
+ class DirectAssessment(BaseModel):
13
+ type: Literal["direct_assessment"]
14
+ criteria: List[Criteria]
15
+ prompt: str
16
+
17
+
18
+ class PairwiseComparison(BaseModel):
19
+ type: Literal["pairwise_comparison"]
20
+ granularity: Literal["coarse", "fine", "super fine"]
21
+ ties_allowed: bool
22
+ position_swapping: bool
23
+ reference_model: str
24
+ prompt: str
25
+
26
+
27
+ class JudgingConfig(BaseModel):
28
+ assessment: Union[DirectAssessment, PairwiseComparison]
prompts.py ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from judging_dataclasses import Criteria
2
+
3
+
4
+ DEFAULT_AGGREGATOR_PROMPT = """We are trying to come up with the best response to a user query based on an aggregation of other responses.
5
+
6
+ [USER PROMPT START]
7
+ {user_prompt}
8
+ [USER PROMPT END]
9
+
10
+ Responses from other LLMs:
11
+ {responses_from_other_llms}
12
+
13
+ Please provide a response the combines the best aspects of the responses above."""
14
+
15
+
16
+ DEFAULT_DIRECT_ASSESSMENT_PROMPT = """We are trying to assess the quality of a response to a user query.
17
+
18
+ [USER PROMPT START]
19
+ {user_prompt}
20
+ [USER PROMPT END]
21
+
22
+ The response is as follows:
23
+
24
+ [RESPONSE START]
25
+ {response}
26
+ [RESPONSE END]
27
+
28
+ Please evaluate the quality of the response based on the following criteria:
29
+
30
+ {criteria_list}
31
+
32
+ Options:
33
+ {options}
34
+
35
+ For each of the criterion, provide a short explanation describing how you would evaluate the response based on that criterion. Then, provide your final rating for that criterion."""
36
+
37
+ DEFAULT_DIRECT_ASSESSMENT_CRITERIA_LIST = [
38
+ Criteria(
39
+ name="helpfulness",
40
+ description="Provides meaningful information and clear solutions that address the query.",
41
+ min_score=1,
42
+ max_score=7,
43
+ ),
44
+ Criteria(
45
+ name="relevance",
46
+ description="Stays on topic and directly relates to the query without unnecessary details.",
47
+ min_score=1,
48
+ max_score=7,
49
+ ),
50
+ Criteria(
51
+ name="conciseness",
52
+ description="Communicates clearly and efficiently, avoiding excess content while retaining substance.",
53
+ min_score=1,
54
+ max_score=7,
55
+ ),
56
+ ]
57
+
58
+ # 7-point likert scale.
59
+ SEVEN_POINT_DIRECT_ASSESSMENT_OPTIONS = [
60
+ "Strongly Disagree",
61
+ "Disagree",
62
+ "Slightly Disagree",
63
+ "Neither Agree Nor Disagree",
64
+ "Slightly Agree",
65
+ "Agree",
66
+ "Strongly Agree",
67
+ ]
68
+
69
+ # 6-point likert scale.
70
+ SIX_POINT_DIRECT_ASSESSMENT_OPTIONS = [
71
+ "Strongly Disagree",
72
+ "Disagree",
73
+ "Slightly Disagree",
74
+ "Slightly Agree",
75
+ "Agree",
76
+ "Strongly Agree",
77
+ ]
78
+
79
+ # 5-point likert scale.
80
+ FIVE_POINT_DIRECT_ASSESSMENT_OPTIONS = [
81
+ "Strongly Disagree",
82
+ "Disagree",
83
+ "Neither Agree Nor Disagree",
84
+ "Agree",
85
+ "Strongly Agree",
86
+ ]
87
+
88
+ # 4-point likert scale.
89
+ FOUR_POINT_DIRECT_ASSESSMENT_OPTIONS = [
90
+ "Strongly Disagree",
91
+ "Disagree",
92
+ "Agree",
93
+ "Strongly Agree",
94
+ ]
95
+
96
+ # 3-point likert scale.
97
+ THREE_POINT_DIRECT_ASSESSMENT_OPTIONS = [
98
+ "Disagree",
99
+ "Neither Agree Nor Disagree",
100
+ "Agree",
101
+ ]
102
+
103
+ # 2-point likert scale.
104
+ BINARY_DIRECT_ASSESSMENT_OPTIONS = [
105
+ "Disagree",
106
+ "Agree",
107
+ ]
108
+
109
+
110
+ DEFAULT_PAIRWISE_COMPARISON_PROMPT = """We are trying to compare the quality of two responses to a user query.
111
+
112
+ [USER PROMPT START]
113
+ {prompt}
114
+ [USER PROMPT END]
115
+
116
+ [RESPONSE A START]
117
+ {first_completion}
118
+ [RESPONSE A END]
119
+
120
+ [RESPONSE B START]
121
+ {second_completion}
122
+ [RESPONSE B END]
123
+
124
+ Begin your evaluation by comparing the two responses and provide a short explanation. Some themes to consider in your evaluation: {themes_to_consider}.
125
+
126
+ After providing your explanation, output your final verdict as one of the following options:
127
+ {pairwise_comparison_options}
128
+ """
129
+
130
+ DEFAULT_PAIRWISE_COMPARISON_THEMES_TO_CONSIDER = [
131
+ (
132
+ "helpfulness",
133
+ "Provides meaningful information and clear solutions that address the query.",
134
+ ),
135
+ (
136
+ "relevance",
137
+ "Stays on topic and directly relates to the query without unnecessary details.",
138
+ ),
139
+ (
140
+ "conciseness",
141
+ "Communicates clearly and efficiently, avoiding excess content while retaining substance.",
142
+ ),
143
+ ]
144
+
145
+ # COARSE WITH TIE.
146
+ DEFAULT_PAIRWISE_COMPARISON_OPTIONS = [
147
+ ("A>B", "Response A is better than Response B"),
148
+ ("B<A", "Response B is better than Response A"),
149
+ ("A=B", "Both responses are equally good"),
150
+ ]