From API to Action: A Multi-Model Comparison of OpenAI, Anthropic, Google, and Meta LLMs for Clinical Trial Data Extraction.
The 14 matches
- [1] § Appendix A. API Fields Retrieved from ClinicalTrials.gov ↔ notebooks/01_dataset_pull.ipynb, lines 191–265 · score 1.00 · BaselineCategoryTitle, OtherOutcomeDescription, BriefTitle, CompletionDate, ConditionMeshTerm, EligibilityCriteria
- [2] § Appendix B. Extraction Prompt and JSON Schema ↔ notebooks/03_LLM_processor.ipynb, lines 547–639 · score 0.95 · brain_stimulation_used, confidence_level, is_noninvasive, primary_type, primary_target, secondary_targets
- [3] § 2. Materials and Methods › 2.4. LLM Configuration and Architecture ↔ notebooks/03_LLM_processor.ipynb, lines 166–297 · score 0.79 · max token, meta llama, OpenRouter API, temperature, configured, OpenAI
- [4] § 2. Materials and Methods › 2.1. Data Source and Collection ↔ notebooks/01_dataset_pull.ipynb, lines 467–584 · score 0.74 · NCT IDs, eligibility criteria, batch, enrollment, location, status
- [5] § 2. Materials and Methods › 2.2. Search Strategy and Trial Selection ↔ figures/make_figure1_search_strategy.py, lines 10–29 · score 0.72 · Parkinson disease tdcs, 19–20, interface, July, variability, clinical
- [6] § 2. Materials and Methods › 2.6. Statistical Analysis ↔ notebooks/paper_stats_v2.ipynb, lines 250–341 · score 0.71 · Pearson correlation, Absolute Error, confusion matrices, milliamperes, MAE, numeric
- [7] § 2. Materials and Methods › 2.2. Search Strategy and Trial Selection ↔ figures/make_figure1_search_strategy.py, lines 10–29 · score 0.66 · Web interface variability, identical clinical queries, 19–20, Parkinson, July, tDCS
- [8] § 2. Materials and Methods › 2.2. Search Strategy and Trial Selection ↔ figures/make_figure1_search_strategy.py, lines 47–61 · score 0.64 · regex filter, tDCS, NCT02349789, NCT07010328, NCT03217110, NCT03221413
- [9] § 2. Materials and Methods › 2.6. Statistical Analysis ↔ notebooks/paper_stats_v2.ipynb, lines 106–176 · score 0.63 · motor cortex, tDCS, canonical, transcranial, brain stimulation, class
- [10] § 2. Materials and Methods › 2.5. Prompt Design and JSON Schema ↔ notebooks/03_LLM_processor.ipynb, lines 547–639 · score 0.62 · brain regions, relevant quotes, Medium, brain stimulation, noninvasive, confidence
- [11] § 3. Results › 3.6. Validation Against an Expert Gold Standard ↔ notebooks/paper_stats_v2.ipynb, lines 106–176 · score 0.61 · primary motor cortex, tDCS, canonical, M1, brain stimulation, synonym
- [12] § 3. Results › 3.1. Overall Agreement Patterns ↔ notebooks/paper_stats_table_2.ipynb, lines 176–291 · score 0.57 · intraclass correlation, pairwise comparisons, ICC, rater, intensity, duration
- [13] § 2. Materials and Methods › 2.5. Prompt Design and JSON Schema ↔ notebooks/03_LLM_processor.ipynb, lines 919–996 · score 0.56 · BriefSummary, DetailedDescription, brain stimulation, LLM, Prompt, field
- [14] § 2. Materials and Methods › 2.6. Statistical Analysis ↔ notebooks/paper_stats_table_2.ipynb, lines 176–291 · score 0.55 · intraclass correlation, ICC, raters, variables, Pairwise, score
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 1,700 lines · 59 KB · MIT · 4 matches
- # %%
- import os
- import pandas as pd
- import numpy as np
- import openai
- import requests
- import pandas as pd
- import openai
- import json
- import logging
- import time
- from typing import List, Dict, Any, Optional
- from enum import Enum
- from dataclasses import dataclass
- import openrouter
- # %%
- # Import the openrouter module
- # Store API key in a separate file
- with open('../config/openrouter_api_key.txt', 'r') as f:
- api_key = f.read().strip()
- openrouter.api_key = api_key
- # %%
- # Store API key in a separate file
- with open('../config/api_key.txt', 'r') as f:
- api_key = f.read().strip()
- openai.api_key = api_key
- # %% [markdown]
- # %%
- # disease = 'Parkinson'
- # %%
- # Define file path
- file_path_parquet = os.path.join('../data/filtered', '02_simple_filter.parquet')
- # Load the DataFrame from the Parquet file
- filtered_df_covariates = pd.read_parquet(file_path_parquet)
- # Display the DataFrame
- print(filtered_df_covariates)
- # %%
- print(filtered_df_covariates.columns)
- # %%
- #test openai
- # response = openai.chat.completions.create(
- # model='gpt-3.5-turbo',
- # messages=[
- # {"role": "user", "content": "Hello, how are you?"}
- # ],
- # max_tokens=50,
- # temperature=0.5,
- # )
- # print(response.choices[0].message.content.strip())
- # %%
- # import requests
- # import json
- # # Configuration Variables
- # API_KEY = openrouter.api_key # Ensure this is defined
- # SITE_URL = 'https://pd-research.com' # Your site's URL
- # APP_NAME = 'PD Research' # Your application's name
- # def send_test_prompt():
- # print("Starting the API request...") # Debugging statement
- # try:
- # # Prepare the payload
- # payload = {
- # "model": "openai/o1-mini-2024-09-12", # Updated model
- # "messages": [
- # {
- # "role": "user",
- # "content": "What is the average age Parkinson's disease starts?"
- # }
- # ],
- # "max_tokens": 150, # Increased max_tokens
- # "temperature": 0.7, # Increased temperature for more varied responses
- # }
- # # Make the POST request to the OpenRouter API
- # response = requests.post(
- # url="https://openrouter.ai/api/v1/chat/completions",
- # headers={
- # "Authorization": f"Bearer {API_KEY}",
- # "HTTP-Referer": SITE_URL, # Optional: Include if you want your app featured
- # "X-Title": APP_NAME, # Optional: Shows your app name on OpenRouter rankings
- # },
- # data=json.dumps(payload)
- # )
- # print("API request sent. Awaiting response...") # Debugging statement
- # # Check if the request was successful
- # if response.status_code == 200:
- # print("Received successful response from the API.") # Debugging statement
- # response_data = response.json()
- # # Print the full JSON response for debugging
- # print("Full Response JSON:")
- # print(json.dumps(response_data, indent=4))
- # # Extract the assistant's reply
- # try:
- # assistant_reply = response_data['choices'][0]['message']['content'].strip()
- # print(f"Assistant's reply: {assistant_reply}")
- # except (KeyError, IndexError) as e:
- # print("Error extracting assistant's reply:", e)
- # print("Full response:", json.dumps(response_data, indent=4))
- # else:
- # print(f"Request failed with status code {response.status_code}")
- # print("Response:", response.text)
- # except requests.exceptions.RequestException as e:
- # print("An error occurred while making the request:", e)
- # # --- Execute the Test Prompt ---
- # send_test_prompt()
- # %%
- def generate_prompt(self, context: str) -> str:
- """Generate analysis prompt from context"""
- return f"""
- Analyze whether brain stimulation was used in this trial. If so, provide details.
- IMPORTANT: Respond ONLY with a JSON object in the EXACT format below. Do not include any additional text or explanations.
- {{
- "brain_stimulation_used": "Yes" or "No",
- "stimulation_details": {{
- "primary_type": "e.g., tDCS, TMS, tACS, DBS, etc." or null,
- "is_noninvasive": true or false,
- "primary_target": "Primary brain region or null",
- "secondary_targets": ["List of secondary regions"] or [],
- "stimulation_parameters": {{
- "intensity": "e.g., 2mA" or null,
- "duration": "e.g., 20 minutes" or null
- }}
- }},
- "confidence_level": "High", "Medium", or "Low",
- "relevant_quotes": ["Direct quotes supporting the analysis"]
- }}
- Context:
- {context}
- """
- # %%
- import openai
- import os
- import time
- from autogen import ConversableAgent, UserProxyAgent
- from autogen.agentchat.contrib.capabilities.teachability import Teachability
- ###############################################################################
- # 1) Hardcode your OpenRouter API key and base URL
- ###############################################################################
- openrouter_api_key = "sk-or-v1-REPLACE_ME_WITH_REAL_KEY"
- openai.api_key = openrouter_api_key
- openai.api_base = "https://openrouter.ai/api/v1"
- # Optionally set extra OpenRouter headers
- openai.request_headers = {
- "Authorization": f"Bearer {openrouter_api_key}",
- "X-Title": "MyMultiAgentApp",
- "HTTP-Referer": "https://example.com",
- }
- ###############################################################################
- # 2) Define multiple model names that are known to work with your key
- # (If you don't have GPT-4 access, remove "gpt-4".)
- ###############################################################################
- model_list = [
- "gpt-4",
- "anthropic/claude-3.5-sonnet",
- "meta-llama/llama-3-70b-instruct",
- ]
- ###############################################################################
- # 3) Build an agent for each model, with Teachability
- ###############################################################################
- def build_agent_for_model(model_name: str):
- """
- Creates a ConversableAgent configured for a specific model.
- Attaches Teachability with a local vector DB folder
- unique to the model_name.
- """
- config_list = [
- {
- "model": model_name,
- "temperature": 0.5, # or any you prefer
- "max_tokens": 4096, # might need a bigger limit for some models
- "top_p": 1.0,
- }
- ]
- agent = ConversableAgent(
- name=f"agent_{model_name}",
- llm_config={
- "config_list": config_list,
- "timeout": 120,
- "cache_seed": None,
- },
- )
- # Use a unique DB path for each model
- db_path = f"./tmp/db_{model_name.replace('/','_').replace('-','_')}"
- teachability = Teachability(
- verbosity=1,
- reset_db=True, # Clears any prior memos each run
- path_to_db_dir=db_path,
- recall_threshold=1.3,
- )
- teachability.add_to_agent(agent)
- return agent
- ###############################################################################
- # 4) Main Demo
- ###############################################################################
- def main():
- # Hardcoded sports betting text for "teaching"
- sports_betting_text = """\
- A spread bet in sports betting is a wager based on the margin of victory in a game,
- commonly used in popular sports like football and basketball. When you bet on the spread,
- you're predicting how much a team will win or lose by.
- If you bet on the favorite (shown with a '-' sign), that team must win by more than the
- specified point spread for your bet to succeed. If you bet on the underdog (shown with a '+' sign),
- they can lose by fewer points than the spread — or win outright — for your bet to win.
- The spread helps balance the odds between teams, making the bet more about predicting
- the margin rather than just picking a winner.
- Example: If the Bears are -6.5 points against the Packers, the Bears must win by more than 6.5
- points for a Bears -6.5 bet to cash. If the game ends with the Bears winning by exactly 6 points,
- that bet loses. If the underdog (Packers) loses by fewer than 6.5, an underdog bet would win.
- """
- # Create one user proxy. We'll call user.initiate_chat(...) ourselves.
- user = UserProxyAgent(
- name="user",
- human_input_mode="NEVER", # We handle input() in Python code
- max_consecutive_auto_reply=0,
- )
- # Build an agent for each model
- agents = {}
- for m in model_list:
- agents[m] = build_agent_for_model(m)
- # 4a) Teach each agent the sports-betting text once
- for model_name, agent in agents.items():
- print(f"\n>>> Teaching {model_name} the sports-betting text...\n")
- teach_resp = user.initiate_chat(agent, message=sports_betting_text, clear_history=True)
- print(f"{model_name} replied:", teach_resp.content)
- # 4b) Now we do a Q&A loop: user types question, all agents respond
- print("\n=== Multi-Model Q&A ===")
- print("Type 'exit' to quit.\n")
- while True:
- q = input("You: ").strip()
- if q.lower() in ["exit", "quit"]:
- print("Goodbye!")
- break
- for model_name, agent in agents.items():
- # Start a new chat to confirm it can recall the taught text
- # Or you can do clear_history=False so it accumulates Q&A
- answer = user.initiate_chat(agent, message=q, clear_history=True)
- print(f"\n[{model_name} responds]\n{answer.content}\n")
- # Optional: small delay if you want to separate them visually
- time.sleep(1)
- if __name__ == "__main__":
- main()
- # %%
- import requests
- resp = requests.post('https://textbelt.com/text', {
- 'phone': '9163802941',
- 'message': 'PD Pipe Line Part 3 Done',
- 'key': '138adc496234ca311154757db147f552afa8ba83FfrCKJ36kTJNXq65nlsvvF4Pu',
- })
- print(resp.json())
- # %%
- 1=2
- # %%
- # %%
- # old working code
- # %%
- import os
- import requests
- import json
- import logging
- import pandas as pd
- import time
- from typing import Dict, List, Optional, Union
- from dataclasses import dataclass
- from enum import Enum
- # Configure logging
- logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s - %(levelname)s - %(message)s',
- datefmt='%Y-%m-%d %H:%M:%S'
- )
- class APIError(Exception):
- """Custom exception for API-related errors"""
- pass
- class ValidationError(Exception):
- """Custom exception for response validation errors"""
- pass
- class BrainStimStatus(str, Enum):
- YES = "Yes"
- NO = "No"
- @dataclass
- class StimulationParameters:
- frequency: Optional[str] = None
- intensity: Optional[str] = None
- duration: Optional[str] = None
- @dataclass
- class StimulationDetails:
- primary_type: Optional[str] = None
- is_noninvasive: Optional[bool] = None
- primary_target: Optional[str] = None
- secondary_targets: List[str] = None
- stimulation_parameters: StimulationParameters = None
- def __post_init__(self):
- if self.secondary_targets is None:
- self.secondary_targets = []
- if self.stimulation_parameters is None:
- self.stimulation_parameters = StimulationParameters()
- @dataclass
- class BrainStimResponse:
- brain_stimulation_used: BrainStimStatus
- stimulation_details: StimulationDetails
- confidence_level: str
- relevant_quotes: List[str]
- class BrainStimAnalyzer:
- def __init__(self, api_key: str, site_url: str, app_name: str):
- self.api_key = api_key
- self.site_url = site_url
- self.app_name = app_name
- self.models = {
- "gpt-4": {
- "requires_image": False,
- "max_tokens": 4096,
- "temperature": 0.5,
- "instructions": "Return ONLY a JSON object with no additional explanatory text."
- },
- "anthropic/claude-3.5-sonnet": {
- "requires_image": False,
- "max_tokens": 10000,
- "temperature": 0.5,
- "instructions": "Return ONLY a JSON object with no additional explanatory text."
- },
- "meta-llama/llama-3-70b-instruct": {
- "requires_image": False,
- "max_tokens": 4096,
- "temperature": 0.5,
- "instructions": "Return ONLY a JSON object with no additional explanatory text."
- }
- }
- def extract_json_from_text(self, text: str) -> Optional[str]:
- """Extract JSON object from text that may contain additional content"""
- try:
- # Look for JSON-like content between curly braces
- start = text.find('{')
- end = text.rfind('}')
- if start != -1 and end != -1:
- json_str = text[start:end+1]
- # Validate it's proper JSON
- json.loads(json_str)
- return json_str
- except json.JSONDecodeError:
- pass
- return None
- def validate_brain_stim_response(self, response_json: Dict) -> BrainStimResponse:
- """Validate response structure and content"""
- required_fields = [
- "brain_stimulation_used",
- "stimulation_details",
- "confidence_level",
- "relevant_quotes"
- ]
- # Check required fields
- for field in required_fields:
- if field not in response_json:
- raise ValidationError(f"Missing required field: {field}")
- # Validate brain_stimulation_used
- if not isinstance(response_json["brain_stimulation_used"], str):
- raise ValidationError("brain_stimulation_used must be string")
- if response_json["brain_stimulation_used"] not in ["Yes", "No"]:
- raise ValidationError("brain_stimulation_used must be 'Yes' or 'No'")
- # Convert to BrainStimResponse object
- stim_params = StimulationParameters(**response_json["stimulation_details"]["stimulation_parameters"])
- stim_details = StimulationDetails(
- primary_type=response_json["stimulation_details"]["primary_type"],
- is_noninvasive=response_json["stimulation_details"]["is_noninvasive"],
- primary_target=response_json["stimulation_details"]["primary_target"],
- secondary_targets=response_json["stimulation_details"]["secondary_targets"],
- stimulation_parameters=stim_params
- )
- return BrainStimResponse(
- brain_stimulation_used=BrainStimStatus(response_json["brain_stimulation_used"]),
- stimulation_details=stim_details,
- confidence_level=response_json["confidence_level"],
- relevant_quotes=response_json["relevant_quotes"]
- )
- def send_request(self, model_name: str, prompt: str, max_retries: int = 3) -> BrainStimResponse:
- """Send request to API with retry logic"""
- if model_name not in self.models:
- raise ValueError(f"Unknown model: {model_name}")
- model_config = self.models[model_name]
- prompt = f"{model_config['instructions']}\n{prompt}"
- retries = 0
- backoff = 2 # seconds
- while retries < max_retries:
- try:
- response = requests.post(
- url="https://openrouter.ai/api/v1/chat/completions",
- headers={
- "Authorization": f"Bearer {self.api_key}",
- "HTTP-Referer": self.site_url,
- "X-Title": self.app_name,
- "Content-Type": "application/json"
- },
- json={
- "model": model_name,
- "messages": [{"role": "user", "content": prompt}],
- "temperature": model_config["temperature"],
- "max_tokens": model_config["max_tokens"]
- },
- timeout=60
- )
- logging.info(f"Sent request to {model_name}")
- logging.debug(f"Raw response: {response.text}")
- if response.status_code == 200:
- try:
- response_data = response.json()
- # Check if 'choices' and necessary keys exist in the response
- if 'choices' in response_data and len(response_data['choices']) > 0:
- choice = response_data['choices'][0]
- if 'message' in choice and 'content' in choice['message']:
- content = choice['message']['content'].strip()
- # Try to parse as JSON directly first
- try:
- response_json = json.loads(content)
- except json.JSONDecodeError:
- # If direct parsing fails, try to extract JSON from text
- json_str = self.extract_json_from_text(content)
- if not json_str:
- raise ValidationError(f"Could not extract valid JSON from response: {content}")
- response_json = json.loads(json_str)
- # Validate response structure and content
- return self.validate_brain_stim_response(response_json)
- else:
- raise ValidationError("Response does not contain 'message' or 'content' keys.")
- else:
- raise ValidationError("Response does not contain 'choices' or it's empty.")
- except (KeyError, IndexError) as e:
- logging.error(f"Error extracting response content: {e}")
- raise ValidationError(f"Invalid response structure from {model_name}")
- elif response.status_code in [429, 500, 502, 503, 504]:
- if retries == max_retries - 1:
- raise APIError(f"Max retries reached for {model_name}")
- retries += 1
- time.sleep(backoff)
- backoff *= 2
- continue
- else:
- raise APIError(f"Request failed with status {response.status_code}: {response.text}")
- except requests.exceptions.RequestException as e:
- if retries == max_retries - 1:
- raise APIError(f"Request failed after {max_retries} retries: {str(e)}")
- retries += 1
- time.sleep(backoff)
- backoff *= 2
- continue
- raise APIError(f"Failed to get valid response from {model_name}")
- class BrainStimAnalysis:
- def __init__(self, api_key: str, site_url: str, app_name: str):
- self.analyzer = BrainStimAnalyzer(api_key, site_url, app_name)
- def process_trial(self, df: pd.DataFrame, output_dir: str = '03_data_ai'):
- """Process trial data and save results"""
- os.makedirs(output_dir, exist_ok=True)
- result_df = df.copy()
- # Initialize response arrays
- responses = []
- # Process each row
- for idx, row in df.iterrows():
- context = row.get('DetailedDescription', row.get('BriefSummary', ''))
- prompt = self.generate_prompt(context)
- # Get responses from each model
- for model_name in self.analyzer.models.keys():
- try:
- response = self.analyzer.send_request(model_name, prompt)
- responses.append({
- "Model": model_name,
- "Response": response
- })
- # Update DataFrame with response data
- self.update_dataframe(result_df, idx, model_name, response)
- except (APIError, ValidationError) as e:
- logging.error(f"Error processing row {idx} with model {model_name}: {str(e)}")
- continue
- # Save results
- self.save_results(result_df, output_dir)
- return result_df, responses
- def generate_prompt(self, context: str) -> str:
- """Generate analysis prompt from context"""
- return f"""
- Analyze whether brain stimulation was used in this trial. If so, provide details.
- IMPORTANT: Respond ONLY with a JSON object in the EXACT format below. Do not include any additional text or explanations.
- {{
- "brain_stimulation_used": "Yes" or "No",
- "stimulation_details": {{
- "primary_type": "e.g., tDCS, TMS, tACS, DBS, etc." or null,
- "is_noninvasive": true or false,
- "primary_target": "Primary brain region or null",
- "secondary_targets": ["List of secondary regions"] or [],
- "stimulation_parameters": {{
- "frequency": "e.g., 10Hz" or null,
- "intensity": "e.g., 2mA" or null,
- "duration": "e.g., 20 minutes" or null
- }}
- }},
- "confidence_level": "High", "Medium", or "Low",
- "relevant_quotes": ["Direct quotes supporting the analysis"]
- }}
- Context:
- {context}
- """
- def update_dataframe(self, df: pd.DataFrame, idx: int, model_name: str, response: BrainStimResponse):
- """Update DataFrame with response data"""
- suffix = model_name.replace("/", "_").replace("-", "_")
- # Update brain stimulation status
- df.at[idx, f"brain_stimulation_used_{suffix}"] = response.brain_stimulation_used.value
- # Update stimulation details
- if response.stimulation_details:
- df.at[idx, f"stimulation_details_primary_type_{suffix}"] = response.stimulation_details.primary_type
- df.at[idx, f"stimulation_details_is_noninvasive_{suffix}"] = response.stimulation_details.is_noninvasive
- df.at[idx, f"stimulation_details_primary_target_{suffix}"] = response.stimulation_details.primary_target
- df.at[idx, f"stimulation_details_secondary_targets_{suffix}"] = json.dumps(response.stimulation_details.secondary_targets)
- # Update parameters
- params = response.stimulation_details.stimulation_parameters
- df.at[idx, f"stimulation_details_parameters_frequency_{suffix}"] = params.frequency
- df.at[idx, f"stimulation_details_parameters_intensity_{suffix}"] = params.intensity
- df.at[idx, f"stimulation_details_parameters_duration_{suffix}"] = params.duration
- # Update confidence and quotes
- df.at[idx, f"confidence_level_{suffix}"] = response.confidence_level
- df.at[idx, f"relevant_quotes_{suffix}"] = json.dumps(response.relevant_quotes)
- def save_results(self, df: pd.DataFrame, output_dir: str):
- """Save results to files"""
- df.to_excel(f'{output_dir}/part_3_covariates_done.xlsx', index=False)
- df.to_parquet(f'{output_dir}/part_3_covariates_done.parquet', index=False)
- def main():
- # Configuration
- api_key = openrouter.api_key
- site_url = 'https://pd-research.com'
- app_name = 'PD Research'
- if not api_key:
- logging.error("API_KEY is not set. Please set the OPENROUTER_API_KEY environment variable.")
- exit(1)
- try:
- # Load your DataFrame (replace with actual loading logic)
- df = filtered_df_covariates ## Update with your file path
- # Initialize and run analysis
- analysis = BrainStimAnalysis(api_key, site_url, app_name)
- result_df, responses = analysis.process_trial(df)
- # Save result_df and responses to Excel and Parquet formats
- output_dir = '03_data_ai'
- os.makedirs(output_dir, exist_ok=True)
- # Save result_df
- result_df.to_excel(f'{output_dir}/part_3_covariates_done_v2.xlsx', index=False)
- result_df.to_parquet(f'{output_dir}/part_3_covariates_done_v2.parquet', index=False)
- # Convert responses to DataFrame and save
- responses_df = pd.DataFrame(responses)
- responses_df.to_excel(f'{output_dir}/responses_done_v2.xlsx', index=False)
- responses_df.to_parquet(f'{output_dir}/responses_done_v2.parquet', index=False)
- logging.info(f"Results saved to {output_dir}/part_3_covariates_done_v2.xlsx and {output_dir}/responses_done_v2.xlsx")
- # Display results
- print("\n=== Updated LLM Responses ===")
- print(result_df)
- # Optional: Display using PrettyTable
- try:
- from prettytable import PrettyTable
- table = PrettyTable()
- table.field_names = ["Model", "Response"]
- for resp in responses:
- table.add_row([resp["Model"], resp["Response"]])
- print("\n=== LLM Responses (PrettyTable) ===")
- print(table)
- except ImportError:
- logging.warning("PrettyTable not installed. Skipping table formatting.")
- except Exception as e:
- logging.error(f"Error in main execution: {str(e)}")
- raise
- # first_five.to_excel('filtered_df_covariates_updated.xlsx', index=False)
- # logging.info("Updated DataFrame has been saved to filtered_df_covariates_updated.xlsx")
- if __name__ == "__main__":
- main()
- # %% [markdown]
- # old code working
- # %%
- # Save the updated DataFrame to files in folder '03_data_ai'
- responses.to_excel('03_data_ai/part_3_covariates_done_v2.xlsx', index=False)
- # result_df.to_parquet('03_data_ai/part_3_covariates_done_v2.parquet', index=False)
- # %%
- 1=2
- # %%
- import os
- import requests
- import json
- import logging
- import pandas as pd
- import time
- # Configure logging
- logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(message)s')
- # Configuration Variables
- API_KEY = openrouter.api_key
- if not API_KEY:
- logging.error("API_KEY is not set. Please set the OPENROUTER_API_KEY environment variable.")
- exit(1)
- SITE_URL = 'https://pd-research.com' # Your site's URL
- APP_NAME = 'PD Research' # Your application's name
- # Define the models to query with their specific settings
- MODELS = {
- "gpt-4": {
- "requires_image": False,
- "payload_modifier": lambda content: content, # No modification needed
- "max_tokens": 4096, # Example max_tokens for GPT-4
- "temperature": 0.5 # Lower temperature for accuracy
- },
- "anthropic/claude-3.5-sonnet": {
- "requires_image": False, # Changed to False as per your requirement
- "payload_modifier": lambda content: content, # No modification needed
- "max_tokens": 10000, # Example max_tokens for Claude 3.5
- "temperature": 0.5 # Lower temperature for accuracy
- },
- "meta-llama/llama-3-70b-instruct": {
- "requires_image": False,
- "payload_modifier": lambda content: content, # No modification needed
- "max_tokens": 4096, # Example max_tokens for Qwen
- "temperature": 0.5 # Lower temperature for accuracy
- }
- }
- # Define colors for each model (Optional: Requires colorama)
- try:
- from colorama import Fore, Style
- COLORAMA_AVAILABLE = True
- except ImportError:
- COLORAMA_AVAILABLE = False
- logging.warning("Colorama not installed. Responses will not be color-coded.")
- MODEL_COLORS = {
- "gpt-4": Fore.BLUE if COLORAMA_AVAILABLE else "",
- "anthropic/claude-3.5-sonnet": Fore.MAGENTA if COLORAMA_AVAILABLE else "",
- "meta-llama/llama-3-70b-instruct": Fore.GREEN if COLORAMA_AVAILABLE else ""
- }
- def send_request(model_name, prompt):
- """
- Sends a request to the specified model with the given prompt.
- Parameters:
- model_name (str): The name of the model to query.
- prompt (str): The text prompt/question.
- Returns:
- str: The assistant's reply or an error message.
- """
- logging.info(f"Sending request to model: {model_name}")
- # For text-only models, no modification is needed
- content = prompt
- modified_content = MODELS[model_name]["payload_modifier"](content)
- # Construct the payload with per-model settings
- payload = {
- "model": model_name,
- "messages": [
- {
- "role": "user",
- "content": modified_content
- }
- ],
- "top_p": 1,
- "temperature": MODELS[model_name]["temperature"], # Use per-model temperature
- "frequency_penalty": 0,
- "presence_penalty": 0,
- "repetition_penalty": 1,
- "top_k": 0,
- "max_tokens": MODELS[model_name]["max_tokens"], # Use per-model max_tokens
- }
- retries = 3
- backoff = 2 # seconds
- for attempt in range(1, retries + 1):
- try:
- # Make the POST request to the OpenRouter API
- response = requests.post(
- url="https://openrouter.ai/api/v1/chat/completions",
- headers={
- "Authorization": f"Bearer {API_KEY}",
- "HTTP-Referer": SITE_URL, # Optional: Include if you want your app featured
- "X-Title": APP_NAME, # Optional: Shows your app name on OpenRouter rankings
- "Content-Type": "application/json" # Ensure content type is set
- },
- data=json.dumps(payload),
- timeout=60 # Increased timeout for models that may take longer
- )
- logging.info("API request sent. Awaiting response...")
- # Check Content-Type
- content_type = response.headers.get('Content-Type', '')
- logging.debug(f"Response Content-Type: {content_type}")
- if 'application/json' not in content_type:
- logging.error(f"Unexpected Content-Type: {content_type}")
- logging.error(f"Response: {response.text}")
- return "Error: Unexpected Content-Type"
- # Check if the request was successful
- if response.status_code == 200:
- logging.info(f"Received successful response from {model_name}.")
- try:
- response_data = response.json()
- # Extract the assistant's reply
- assistant_reply = response_data['choices'][0]['message']['content'].strip()
- logging.info(f"Assistant's reply from {model_name}: {assistant_reply}")
- return assistant_reply
- except (KeyError, IndexError) as e:
- logging.error(f"Error extracting assistant's reply from {model_name}: {e}")
- logging.debug(f"Full response from {model_name}: {json.dumps(response_data, indent=4)}")
- return "Error extracting reply."
- elif response.status_code in [429, 500, 502, 503, 504]:
- # Retry for rate limiting or server errors
- logging.warning(f"Attempt {attempt} failed with status code {response.status_code}. Retrying in {backoff} seconds...")
- time.sleep(backoff)
- backoff *= 2
- else:
- logging.error(f"Request to {model_name} failed with status code {response.status_code}")
- logging.error(f"Response: {response.text}")
- return f"Error: Received status code {response.status_code}"
- except requests.exceptions.RequestException as e:
- logging.error(f"Attempt {attempt} failed with exception: {e}")
- if attempt < retries:
- logging.info(f"Retrying in {backoff} seconds...")
- time.sleep(backoff)
- backoff *= 2
- else:
- logging.error("All retry attempts failed.")
- return f"Error: {e}"
- return "Error: Unable to retrieve response."
- def main():
- # Load your DataFrame
- # Replace the following line with how you load your DataFrame
- # For example, if it's a CSV:
- # filtered_df_covariates = pd.read_csv('filtered_df_covariates.csv')
- # For demonstration, let's assume filtered_df_covariates is already loaded
- # Here's a placeholder:
- # filtered_df_covariates = pd.DataFrame(...) # Your DataFrame loading logic
- # **Important:** Replace the following line with your actual DataFrame loading method
- # For example:
- # filtered_df_covariates = pd.read_csv('your_file.csv')
- # Ensure that 'filtered_df_covariates' is defined before proceeding
- try:
- filtered_df_covariates # Check if the DataFrame is already loaded
- except NameError:
- logging.error("DataFrame 'filtered_df_covariates' is not defined. Please load it before running the script.")
- exit(1)
- # Define the prompt
- prompt_template = """
- Analyze whether brain stimulation was used in this trial. If so, provide details.
- IMPORTANT: Respond ONLY with a JSON object in the EXACT format below. Do not include any additional text or explanations.
- {
- "brain_stimulation_used": "Yes" or "No",
- "stimulation_details": {
- "primary_type": "e.g., tDCS, TMS, tACS, DBS, etc." or null,
- "is_noninvasive": true or false,
- "primary_target": "Primary brain region or null",
- "secondary_targets": ["List of secondary regions"] or [],
- "stimulation_parameters": {
- "frequency": "e.g., 10Hz" or null,
- "intensity": "e.g., 2mA" or null,
- "duration": "e.g., 20 minutes" or null
- }
- },
- "confidence_level": "High", "Medium", or "Low",
- "relevant_quotes": ["Direct quotes supporting the analysis"]
- }
- """
- # Select the first 5 rows
- first_five = filtered_df_covariates.copy()
- # Define new columns for JSON fields with model suffixes
- # We'll create columns dynamically as we process each model
- # Alternatively, initialize them if you know the models beforehand
- # Initialize the responses list
- responses = []
- # Iterate over the first 5 rows
- for idx, row in first_five.iterrows():
- # Extract relevant information from the row to include in the prompt
- # Customize this based on which columns provide context for the analysis
- # For example, using 'DetailedDescription' or 'FullAnalysis'
- context = row['DetailedDescription'] if 'DetailedDescription' in row else row['BriefSummary']
- # Construct the full prompt by appending context to the prompt template
- prompt = f"{prompt_template}\n\nContext:\n{context}"
- # Send the prompt to each model and store responses
- for model in MODELS.keys():
- response = send_request(model, prompt)
- responses.append({
- "Model": model,
- "Response": response
- })
- # Check if the response is a JSON object
- try:
- response_json = json.loads(response)
- except json.JSONDecodeError:
- logging.error(f"Response from {model} is not valid JSON. Response: {response}")
- response_json = {}
- # Define column suffix based on model name
- suffix = model.replace("/", "_").replace("-", "_")
- # Populate the new columns with the JSON data
- if "brain_stimulation_used" in response_json:
- first_five.at[idx, f"brain_stimulation_used_{suffix}"] = response_json.get("brain_stimulation_used", None)
- else:
- first_five.at[idx, f"brain_stimulation_used_{suffix}"] = None
- if "stimulation_details" in response_json and isinstance(response_json["stimulation_details"], dict):
- stim_details = response_json["stimulation_details"]
- first_five.at[idx, f"stimulation_details_primary_type_{suffix}"] = stim_details.get("primary_type", None)
- first_five.at[idx, f"stimulation_details_is_noninvasive_{suffix}"] = stim_details.get("is_noninvasive", None)
- first_five.at[idx, f"stimulation_details_primary_target_{suffix}"] = stim_details.get("primary_target", None)
- # Assign as JSON string to avoid issues with lists
- secondary_targets = stim_details.get("secondary_targets", [])
- first_five.at[idx, f"stimulation_details_secondary_targets_{suffix}"] = json.dumps(secondary_targets)
- stim_params = stim_details.get("stimulation_parameters", {})
- first_five.at[idx, f"stimulation_details_stimulation_parameters_frequency_{suffix}"] = stim_params.get("frequency", None)
- first_five.at[idx, f"stimulation_details_stimulation_parameters_intensity_{suffix}"] = stim_params.get("intensity", None)
- first_five.at[idx, f"stimulation_details_stimulation_parameters_duration_{suffix}"] = stim_params.get("duration", None)
- else:
- # If 'stimulation_details' is missing or not a dict, set defaults
- first_five.at[idx, f"stimulation_details_primary_type_{suffix}"] = None
- first_five.at[idx, f"stimulation_details_is_noninvasive_{suffix}"] = None
- first_five.at[idx, f"stimulation_details_primary_target_{suffix}"] = None
- first_five.at[idx, f"stimulation_details_secondary_targets_{suffix}"] = json.dumps([])
- first_five.at[idx, f"stimulation_details_stimulation_parameters_frequency_{suffix}"] = None
- first_five.at[idx, f"stimulation_details_stimulation_parameters_intensity_{suffix}"] = None
- first_five.at[idx, f"stimulation_details_stimulation_parameters_duration_{suffix}"] = None
- if "confidence_level" in response_json:
- first_five.at[idx, f"confidence_level_{suffix}"] = response_json.get("confidence_level", None)
- else:
- first_five.at[idx, f"confidence_level_{suffix}"] = None
- if "relevant_quotes" in response_json:
- # Assign as JSON string to avoid issues with lists
- relevant_quotes = response_json.get("relevant_quotes", [])
- first_five.at[idx, f"relevant_quotes_{suffix}"] = json.dumps(relevant_quotes)
- else:
- first_five.at[idx, f"relevant_quotes_{suffix}"] = json.dumps([])
- # Display the updated DataFrame
- print("\n=== Updated LLM Responses ===")
- # Select only the new columns for display
- new_columns_display = []
- for model in MODELS.keys():
- suffix = model.replace("/", "_").replace("-", "_")
- new_columns_display.extend([
- f"brain_stimulation_used_{suffix}",
- f"stimulation_details_primary_type_{suffix}",
- f"stimulation_details_is_noninvasive_{suffix}",
- f"stimulation_details_primary_target_{suffix}",
- f"stimulation_details_secondary_targets_{suffix}",
- f"stimulation_details_stimulation_parameters_frequency_{suffix}",
- f"stimulation_details_stimulation_parameters_intensity_{suffix}",
- f"stimulation_details_stimulation_parameters_duration_{suffix}",
- f"confidence_level_{suffix}",
- f"relevant_quotes_{suffix}"
- ])
- print(first_five[new_columns_display])
- # Optional: Using PrettyTable for better console display
- try:
- from prettytable import PrettyTable
- table = PrettyTable()
- table.field_names = ["Model", "Response"]
- for resp in responses:
- table.add_row([resp["Model"], resp["Response"]])
- print("\n=== LLM Responses (PrettyTable) ===")
- print(table)
- except ImportError:
- logging.warning("PrettyTable not installed. Skipping table formatting.")
- # Optional: Using colorama for color-coded console output
- if COLORAMA_AVAILABLE:
- print("\n=== LLM Responses (Color-Coded) ===")
- for resp in responses:
- color = MODEL_COLORS.get(resp["Model"], Fore.WHITE)
- print(f"{color}{resp['Model']}: {resp['Response']}{Style.RESET_ALL}")
- else:
- logging.info("Colorama not available. Skipping color-coded output.")
- # Optional: Save the updated DataFrame to a file
- # Uncomment the following lines to save as CSV or Excel
- # first_five.to_csv('filtered_df_covariates_updated.csv', index=False)
- # Ensure the new columns are in the DataFrame
- for model in MODELS.keys():
- suffix = model.replace("/", "_").replace("-", "_")
- new_columns = [
- f"brain_stimulation_used_{suffix}",
- f"stimulation_details_primary_type_{suffix}",
- f"stimulation_details_is_noninvasive_{suffix}",
- f"stimulation_details_primary_target_{suffix}",
- f"stimulation_details_secondary_targets_{suffix}",
- f"stimulation_details_stimulation_parameters_frequency_{suffix}",
- f"stimulation_details_stimulation_parameters_intensity_{suffix}",
- f"stimulation_details_stimulation_parameters_duration_{suffix}",
- f"confidence_level_{suffix}",
- f"relevant_quotes_{suffix}"
- ]
- for col in new_columns:
- if col not in first_five.columns:
- first_five[col] = None
- # Ensure the directory '03_data_ai' exists
- os.makedirs('03_data_ai', exist_ok=True)
- # Save the updated DataFrame to files in folder '03_data_ai'
- first_five.to_excel('03_data_ai/part_3_covariates_done.xlsx', index=False)
- first_five.to_parquet('03_data_ai/part_3_covariates_done.parquet', index=False)
- # first_five.to_excel('filtered_df_covariates_updated.xlsx', index=False)
- # logging.info("Updated DataFrame has been saved to filtered_df_covariates_updated.xlsx")
- if __name__ == "__main__":
- main()
- # %%
- # # Save the DataFrame to a CSV file
- # filtered_df_covariates.to_csv('03_data_ai/part_3_covariates_done.csv', index=False)
- # filtered_df_covariates.to_excel('03_data_ai/part_3_covariates_done.xlsx', index=False)
- # filtered_df_covariates.to_parquet('03_data_ai/part_3_covariates_done.parquet', index=False)
- # %%
- # Save the DataFrame to an Excel file
- # %%
- 1=2
- # %%
- # WORKING - BELOW÷
- # %%
- # import os
- # import requests
- # import json
- # import logging
- # import pandas as pd
- # import time
- # # Configure logging
- # # logging.basicConfig(level=logging.INFO, format='%(levelname)s:%(message)s')
- # SITE_URL = 'https://pd-research.com' # Your site's URL
- # APP_NAME = 'PD Research' # Your application's name
- # # Define the models to query with their specific settings
- # MODELS = {
- # "gpt-4": {
- # "requires_image": False,
- # "payload_modifier": lambda content: content, # No modification needed
- # "max_tokens": 4096, # Example max_tokens for GPT-4
- # "temperature": 0.5 # Lower temperature for accuracy
- # },
- # "anthropic/claude-3.5-sonnet": {
- # "requires_image": False, # Changed to False as per your requirement
- # "payload_modifier": lambda content: content, # No modification needed
- # "max_tokens": 10000, # Example max_tokens for Claude 3.5
- # "temperature": 0.5 # Lower temperature for accuracy
- # },
- # "qwen/qwen-2.5-72b-instruct": {
- # "requires_image": False,
- # "payload_modifier": lambda content: content, # No modification needed
- # "max_tokens": 4096, # Example max_tokens for Qwen
- # "temperature": 0.5 # Lower temperature for accuracy
- # }
- # }
- # # Define colors for each model (Optional: Requires colorama)
- # try:
- # from colorama import Fore, Style
- # COLORAMA_AVAILABLE = True
- # except ImportError:
- # COLORAMA_AVAILABLE = False
- # logging.warning("Colorama not installed. Responses will not be color-coded.")
- # MODEL_COLORS = {
- # "gpt-4": Fore.BLUE if COLORAMA_AVAILABLE else "",
- # "anthropic/claude-3.5-sonnet": Fore.MAGENTA if COLORAMA_AVAILABLE else "",
- # "qwen/qwen-2.5-72b-instruct": Fore.GREEN if COLORAMA_AVAILABLE else ""
- # }
- # def send_request(model_name, prompt):
- # """
- # Sends a request to the specified model with the given prompt.
- # Parameters:
- # model_name (str): The name of the model to query.
- # prompt (str): The text prompt/question.
- # Returns:
- # str: The assistant's reply or an error message.
- # """
- # logging.info(f"Sending request to model: {model_name}")
- # # For text-only models, no modification is needed
- # content = prompt
- # modified_content = MODELS[model_name]["payload_modifier"](content)
- # # Construct the payload with per-model settings
- # payload = {
- # "model": model_name,
- # "messages": [
- # {
- # "role": "user",
- # "content": modified_content
- # }
- # ],
- # "top_p": 1,
- # "temperature": MODELS[model_name]["temperature"], # Use per-model temperature
- # "frequency_penalty": 0,
- # "presence_penalty": 0,
- # "repetition_penalty": 1,
- # "top_k": 0,
- # "max_tokens": MODELS[model_name]["max_tokens"], # Use per-model max_tokens
- # }
- # retries = 3
- # backoff = 2 # seconds
- # for attempt in range(1, retries + 1):
- # try:
- # # Make the POST request to the OpenRouter API
- # response = requests.post(
- # url="https://openrouter.ai/api/v1/chat/completions",
- # headers={
- # "Authorization": f"Bearer {API_KEY}",
- # "HTTP-Referer": SITE_URL, # Optional: Include if you want your app featured
- # "X-Title": APP_NAME, # Optional: Shows your app name on OpenRouter rankings
- # "Content-Type": "application/json" # Ensure content type is set
- # },
- # data=json.dumps(payload),
- # timeout=60 # Increased timeout for models that may take longer
- # )
- # logging.info("API request sent. Awaiting response...")
- # # Check Content-Type
- # content_type = response.headers.get('Content-Type', '')
- # logging.debug(f"Response Content-Type: {content_type}")
- # if 'application/json' not in content_type:
- # logging.error(f"Unexpected Content-Type: {content_type}")
- # logging.error(f"Response: {response.text}")
- # return "Error: Unexpected Content-Type"
- # # Check if the request was successful
- # if response.status_code == 200:
- # logging.info(f"Received successful response from {model_name}.")
- # try:
- # response_data = response.json()
- # # Extract the assistant's reply
- # assistant_reply = response_data['choices'][0]['message']['content'].strip()
- # logging.info(f"Assistant's reply from {model_name}: {assistant_reply}")
- # return assistant_reply
- # except (KeyError, IndexError) as e:
- # logging.error(f"Error extracting assistant's reply from {model_name}: {e}")
- # logging.debug(f"Full response from {model_name}: {json.dumps(response_data, indent=4)}")
- # return "Error extracting reply."
- # elif response.status_code in [429, 500, 502, 503, 504]:
- # # Retry for rate limiting or server errors
- # logging.warning(f"Attempt {attempt} failed with status code {response.status_code}. Retrying in {backoff} seconds...")
- # time.sleep(backoff)
- # backoff *= 2
- # else:
- # logging.error(f"Request to {model_name} failed with status code {response.status_code}")
- # logging.error(f"Response: {response.text}")
- # return f"Error: Received status code {response.status_code}"
- # except requests.exceptions.RequestException as e:
- # logging.error(f"Attempt {attempt} failed with exception: {e}")
- # if attempt < retries:
- # logging.info(f"Retrying in {backoff} seconds...")
- # time.sleep(backoff)
- # backoff *= 2
- # else:
- # logging.error("All retry attempts failed.")
- # return f"Error: {e}"
- # return "Error: Unable to retrieve response."
- # def main():
- # # Define the prompt (no image URL since all models are text-only)
- # prompt = "What is the average age Parkinson's disease starts?"
- # # Prepare a list to hold the responses
- # responses = []
- # for model in MODELS.keys():
- # reply = send_request(model, prompt)
- # responses.append({
- # "Model": model,
- # "Response": reply
- # })
- # # Create a pandas DataFrame to display the responses
- # df = pd.DataFrame(responses)
- # # Debug: Verify the DataFrame structure
- # logging.debug("DataFrame before pivoting:")
- # logging.debug(df)
- # # Check if 'Model' and 'Response' columns exist
- # if 'Model' not in df.columns or 'Response' not in df.columns:
- # logging.error("DataFrame missing 'Model' or 'Response' columns.")
- # exit(1)
- # # Check for any None or NaN values in 'Model' column
- # if df['Model'].isnull().any():
- # logging.error("Found None or NaN values in 'Model' column.")
- # exit(1)
- # # Set 'Model' as the index and transpose the DataFrame
- # df_pivot = df.set_index('Model').transpose()
- # # Display the DataFrame
- # print("\n=== LLM Responses ===")
- # print(df_pivot.to_string(index=False))
- # # Optional: Using PrettyTable for better console display
- # try:
- # from prettytable import PrettyTable
- # table = PrettyTable()
- # table.field_names = ["Model", "Response"]
- # for resp in responses:
- # table.add_row([resp["Model"], resp["Response"]])
- # print("\n=== LLM Responses (PrettyTable) ===")
- # print(table)
- # except ImportError:
- # logging.warning("PrettyTable not installed. Skipping table formatting.")
- # # Optional: Using colorama for color-coded console output
- # if COLORAMA_AVAILABLE:
- # print("\n=== LLM Responses (Color-Coded) ===")
- # for resp in responses:
- # color = MODEL_COLORS.get(resp["Model"], Fore.WHITE)
- # print(f"{color}{resp['Model']}: {resp['Response']}{Style.RESET_ALL}")
- # else:
- # logging.info("Colorama not available. Skipping color-coded output.")
- # if __name__ == "__main__":
- # main()
- # %%
- # %%
- # %%
- import requests
- resp = requests.post('https://textbelt.com/text', {
- 'phone': '9163802941',
- 'message': 'LLM Processing Done',
- 'key': '138adc496234ca311154757db147f552afa8ba83FfrCKJ36kTJNXq65nlsvvF4Pu',
- })
- print(resp.json())
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- import os
- import requests
- import json
- import logging
- # Configure logging
- logging.basicConfig(level=logging.INFO)
- # Configuration Variables
- API_KEY = openrouter.api_key # Ensure this environment variable is set
- SITE_URL = 'https://pd-research.com' # Your site's URL
- APP_NAME = 'PD Research' # Your application's name
- def send_test_prompt():
- logging.info("Starting the API request...")
- try:
- # Prepare the payload
- payload = {
- "model": "openai/o1-mini",
- "messages": [
- {"role": "user", "content": "What is the average age Parkinson's disease starts?"}
- ],
- "top_p": 1,
- "temperature": 1,
- "frequency_penalty": 0,
- "presence_penalty": 0,
- "repetition_penalty": 1,
- "top_k": 0,
- }
- # Make the POST request to the OpenRouter API
- response = requests.post(
- url="https://openrouter.ai/api/v1/chat/completions",
- headers={
- "Authorization": f"Bearer {API_KEY}",
- "HTTP-Referer": SITE_URL, # Optional: Include if you want your app featured
- "X-Title": APP_NAME, # Optional: Shows your app name on OpenRouter rankings
- },
- data=json.dumps(payload)
- )
- logging.info("API request sent. Awaiting response...")
- # Check if the request was successful
- if response.status_code == 200:
- logging.info("Received successful response from the API.")
- response_data = response.json()
- # Print the full JSON response for debugging
- logging.debug("Full Response JSON:")
- logging.debug(json.dumps(response_data, indent=4))
- # Extract the assistant's reply
- try:
- assistant_reply = response_data['choices'][0]['message']['content'].strip()
- logging.info(f"Assistant's reply: {assistant_reply}")
- except (KeyError, IndexError) as e:
- logging.error("Error extracting assistant's reply:", exc_info=True)
- logging.debug("Full response:", json.dumps(response_data, indent=4))
- else:
- logging.error(f"Request failed with status code {response.status_code}")
- logging.error(f"Response: {response.text}")
- except requests.exceptions.RequestException as e:
- logging.error("An error occurred while making the request:", exc_info=True)
- # --- Execute the Test Prompt ---
- if __name__ == "__main__":
- send_test_prompt()
- # %%
- # %%
- # %%
- # %%
- # %%
- ß
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- 1=2
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- import pandas as pd
- import openai
- # Set your OpenAI API key
- # openai.api_key = 'YOUR_OPENAI_API_KEY' # Replace with your actual API key
- # Function to query OpenAI for each row
- def get_brain_area(row):
- # Prepare the prompt with the relevant data from the row
- prompt = f"""Given the following study data:
- {row.to_json()}
- What area of the brain are they looking for?"""
- try:
- response = openai.chat.completions.create(
- model='gpt-3.5-turbo', # Use 'gpt-4' if you have access
- messages=[
- {"role": "user", "content": prompt}
- ],
- max_tokens=100,
- temperature=0.5,
- )
- # Extract the assistant's reply
- answer = response.choices[0].message.content.strip()
- # Print the response
- print(f"Row {row.name} response: {answer}")
- return answer
- except Exception as e:
- print(f"Error processing row {row.name}: {e}")
- return None
- # Ensure that 'filtered_df_covariates' is defined with your DataFrame
- # For example:
- # filtered_df_covariates = pd.read_csv('your_data.csv')
- # Apply the function to each row and save the result in a new column
- filtered_df_covariates['BrainArea'] = filtered_df_covariates.apply(get_brain_area, axis=1)
- # %%
- import pandas as pd
- import openai
- # Set your OpenAI API key
- # openai.api_key = 'YOUR_OPENAI_API_KEY' # Replace with your actual API key
- # Function to query OpenAI for each row
- def get_brain_area(row):
- # Prepare the prompt with the relevant data from the row
- prompt = f"""Given the following study data:
- {row.to_json()}
- What area of the brain are they looking for?"""
- try:
- response = openai.chat.completions.create(
- model='gpt-3.5-turbo', # Use 'gpt-4' if you have access
- messages=[
- {"role": "user", "content": prompt}
- ],
- max_tokens=100,
- temperature=0.5,
- )
- # Extract the assistant's reply
- answer = response.choices[0].message.content.strip()
- # Print the response
- print(f"Row {row.name} response: {answer}")
- return answer
- except Exception as e:
- print(f"Error processing row {row.name}: {e}")
- return None
- # Ensure that 'filtered_df_covariates' is defined with your DataFrame
- # For example:
- # filtered_df_covariates = pd.read_csv('your_data.csv')
- # Apply the function to each row and save the result in a new column
- filtered_df_covariates['BrainArea'] = filtered_df_covariates.apply(get_brain_area, axis=1)
- # %%
- # %%
- # import pandas as pd
- # import openai
- # # Set your OpenAI API key
- # # Function to query OpenAI for each row
- # def get_brain_area(row):
- # # Prepare the prompt with the relevant data from the row
- # prompt = f"""Given the following study data:
- # {row.to_json()}
- # What area of the brain are they looking for?"""
- # try:
- # response = openai.chat.completions.create(
- # model='gpt-3.5-turbo', # Use 'gpt-4' if you have access
- # messages=[
- # {"role": "user", "content": prompt}
- # ],
- # max_tokens=100,
- # temperature=0.5,
- # )
- # # Extract the assistant's reply
- # answer = response.choices[0].message.content.strip()
- # return answer
- # except Exception as e:
- # print(f"Error processing row {row.name}: {e}")
- # return None
- # # Ensure that 'filtered_df_covariates' is defined with your DataFrame
- # # For example:
- # # filtered_df_covariates = pd.read_csv('your_data.csv')
- # # Apply the function to each row and save the result in a new column
- # filtered_df_covariates['BrainArea'] = filtered_df_covariates.apply(get_brain_area, axis=1)
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- # %%
- import openai
- print(openai.__version__)
- # %%
- # %%
- # %%
- # %%
- # %%
- def _call_local_llama(self, prompt: str) -> str:
- """Call local Llama instance using Ollama API"""
- try:
- # Construct the API endpoint
- api_endpoint = f"{self.local_url}generate"
- # Prepare the request payload
- payload = {
- "model": self.local_model,
- "prompt": prompt,
- "options": {
- "temperature": 0.1
- }
- }
- # Send the POST request
- response = requests.post(api_endpoint, json=payload)
- if response.status_code == 200:
- # Extract the generated text from the response
- result = response.json()
- if 'generated_text' in result:
- return result['generated_text']
- else:
- print(f"Unexpected response structure: {result}")
- return ''
- else:
- raise Exception(f"Local LLM error: Status {response.status_code} - {response.text}")
- except requests.exceptions.RequestException as e:
- raise Exception(f"Network error calling Ollama: {str(e)}")
- except json.JSONDecodeError as e:
- raise Exception(f"Error parsing Ollama response: {str(e)}")
- except Exception as e:
- raise Exception(f"Error calling local Llama: {str(e)}")
- # %%
- # %%
- import openai
- # Your data
- data_json = '{"study": "example study data"}'
- prompt = f"Given the following study data, provide detailed information about the type of study and the brain area involved:\n\n{data_json}"
- # Make the API request to OpenAI
- response = openai.ChatCompletion.create(
- model="gpt-4",
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": prompt}
- ],
- max_tokens=1500,
- n=1,
- stop=None,
- temperature=0.5,
- )
- # Print the response
- print(response['choices'][0]['message']['content'].strip())
03_LLM_processor.ipynb at commit 2e829ad, under MIT · at the source
Overview
- Interdisciplinary Neuroscience, University of Nevada, Las Vegas, NV 89154, USA
- Department of Computer Science, Howard R. Hughes College of Engineering, University of Nevada, Las Vegas, NV 89154, USA
- Department of Kinesiology and Nutrition Sciences, University of Nevada, Las Vegas, NV 89154, USA
Abstract
(1) Background: Clinical trial data extraction from registries such as ClinicalTrials.gov remains labor-intensive and error-prone, often missing critical details hidden in unstructured protocol descriptions. Large Language Models (LLMs) offer potential to automate this process, yet systematic multi-model comparisons on real clinical trial data remain scarce. (2) Methods: Four LLMs (OpenAI o4-mini-high, Anthropic Claude-Sonnet-4, Google Gemini 2.5-Pro, and Meta Llama-4-Maverick) extracted brain stimulation parameters from 67 transcranial direct current stimulation (tDCS) trials in Parkinson’s disease via a structured JSON schema. Pairwise inter-model agreement was quantified with Cohen’s Kappa and percentage agreement across binary, categorical, and multi-component task tiers. (3) Results: Under exact-string matching, agreement was near-perfect for binary classifications (non-invasive classification: 100%; brain stimulation presence: 99.3%, κ = 0.50) and substantial for categorical extractions (primary stimulation type: 96.4%, κ = 0.70), but fell to 48.6% (κ = 0.43) for complex anatomical targets. Numeric parameters revealed model-specific strengths: o4-mini-high and Claude-Sonnet-4 achieved perfect duration agreement (r = 1.000, n = 19) while Llama-4-Maverick diverged substantially (r < 0.12). Validation against an expert gold standard (100% inter-annotator agreement on a 20-trial overlap) confirmed high extraction accuracy across all features (mean 93.7–98.9%). Crucially, the low agreement on anatomical targets proved to be an artifact of exact-string scoring: under the same semantic matching used to measure accuracy, inter-model agreement rose to 97.0%, coinciding with the 95.5% expert accuracy. Inter-model agreement therefore tracks accuracy once both are measured on a common basis. (4) Conclusions: Exact-string inter-model agreement decreases with task complexity, but this decline largely reflects interchangeable free-text wording rather than reduced accuracy. Evaluated semantically, agreement and expert accuracy are both high and closely aligned. A residual risk is not low accuracy but the rare error shared across all models, which agreement cannot detect, and which overall accuracy can itself mask when one class dominates. These findings inform hybrid human–AI systematic review pipelines in which targeted expert oversight focuses on shared-error and minority-class detection.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 14 matches between paragraphs and lines of code.
ricyoung/ClinicalTrialLLM-Extractor
2e829ad114ef2a1ea3f8e48732d8ab6fe5fc5138, 1 July 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
12 files
- figures/
make_figure1_search_stra , Python, 61 lines, 3 matchestegy.py - notebooks/
01_dataset_pull.ipynb , Jupyter, 680 lines, 2 matches - notebooks/
02_simple_filtering.ipyn , Jupyter, 122 linesb - notebooks/
03_LLM_processor.ipynb , Jupyter, 1,700 lines, 4 matches - notebooks/
archive/ , Jupyter, 25 linesUntitled-1.ipynb - notebooks/
archive/ , Jupyter, 442 linestables 2 good/ paper_stats_table_2.ipyn b - notebooks/
archive/ , Jupyter, 245 linestables 2 good/ table-1.ipynb - notebooks/
paper_stats.ipynb , Jupyter, 249 lines - notebooks/
paper_stats_table_2.ipyn , Jupyter, 291 lines, 2 matchesb - notebooks/
paper_stats_v2.ipynb , Jupyter, 915 lines, 3 matches - LICENSE, License, 21 lines
- README.md, Text, 36 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 10 scripts, each with its path and the digest of its content;
- 14 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data Availability Statement
All clinical trial data are publicly available from ClinicalTrials.gov. Processed datasets, analysis code, and the complete extraction pipeline are available at: https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 3 authors, 9 keywords, 112 references.
Cite
This paper
Young, R. J., Fonseca, J., & Poston, B. (2026). From API to Action: A Multi-Model Comparison of OpenAI, Anthropic, Google, and Meta LLMs for Clinical Trial Data Extraction. Bioengineering (Basel, Switzerland), 13(7), 773. https://
BibTeX
@article{young2026api,
author = {Young, Richard J and Fonseca, Jorge and Poston, Brach},
title = {{From API to Action: A Multi-Model Comparison of OpenAI, Anthropic, Google, and Meta LLMs for Clinical Trial Data Extraction}},
journal = {Bioengineering (Basel, Switzerland)},
year = {2026},
month = jul,
volume = {13},
number = {7},
pages = {773},
publisher = {Multidisciplinary Digital Publishing Institute (MDPI)},
issn = {2306-5354},
doi = {10.3390/
url = {https://
pmid = {42510439},
pmcid = {PMC13404533}
}
RIS
TY - JOUR
AU - Young, Richard J
AU - Fonseca, Jorge
AU - Poston, Brach
TI - From API to Action: A Multi-Model Comparison of OpenAI, Anthropic, Google, and Meta LLMs for Clinical Trial Data Extraction
T2 - Bioengineering (Basel, Switzerland)
J2 - Bioengineering (Basel)
PY - 2026
DA - 2026/
VL - 13
IS - 7
SP - 773
SN - 2306-5354
PB - Multidisciplinary Digital Publishing Institute (MDPI)
DO - 10.3390/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.3390/
"type": "article-journal",
"title": "From API to Action: A Multi-Model Comparison of OpenAI, Anthropic, Google, and Meta LLMs for Clinical Trial Data Extraction",
"container-title": "Bioengineering (Basel, Switzerland)",
"author": [
{
"family": "Young",
"given": "Richard J"
},
{
"family": "Fonseca",
"given": "Jorge"
},
{
"family": "Poston",
"given": "Brach"
}
],
"container-title-short":
"volume": "13",
"issue": "7",
"page": "773",
"DOI": "10.3390/
"PMID": "42510439",
"PMCID": "PMC13404533",
"ISSN": "2306-5354",
"publisher": "Multidisciplinary Digital Publishing Institute (MDPI)",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
2
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1162/nol.a.244 [code]
- A Novel Approach to Map the Causal Impact of Brain Stimulation on Semantic Processing With Language Models.Journal: Neurobiology of language (Cambridge, Mass.)In common: Pingouin, scikit-learn, Matplotlib, 1 other tool, other, 1 reference
- [2] doi:10.1038/s41531-026-01380-1 [code]
- Identifying maximal beta power from directional subthalamic local field potentials in Parkinson's disease.Journal: NPJ Parkinson's diseaseIn common: Pingouin, scikit-learn, pandas, 2 other tools, Parkinson's, clinical / translational
- [3] doi:10.1002/alz.71649 [code]
- Postmortem brain MRI reveals differential associations of subcortical and limbic volumes with cortical thinning and neurodegenerative pathologies.Journal: Alzheimer's & dementia : the journal of the Alzheimer's AssociationIn common: Pingouin, scikit-learn, pandas, 2 other tools, Parkinson's
- [4] doi:10.1093/braincomms/fcag218 [code]
- Magnetoencephalography biomarkers for assessing myelin content and neuronal function in acute optic neuritis.Journal: Brain communicationsIn common: Pingouin, scikit-learn, pandas, 2 other tools, other
- [5] doi:10.1016/j.nicl.2026.104012 [code]
- Structural-functional multilayer brain network properties and outcome of combined repetitive transcranial magnetic stimulation and psychotherapy for obsessive-compulsive disorder.Journal: NeuroImage. ClinicalIn common: Pingouin, scikit-learn, pandas, 2 other tools, other
- [6] doi:10.7554/elife.100605 [code]
- Age-related changes in ‘cortical’ 1/
f dynamics are linked to cardiac activity Journal: n/aIn common: Pingouin, scikit-learn, pandas, 2 other tools, other - [7] doi:10.1038/s41398-026-04081-8 [code]
- Functional system-specific brain aging across the Alzheimer's disease continuum.Journal: Translational psychiatryIn common: Pingouin, scikit-learn, pandas, 2 other tools, clinical / translational
- [8] doi:10.1016/j.patter.2026.101538 [code]
- A multi-modal foundation model for brain disease diagnosis and medical imaging.Journal: Patterns (New York, N.Y.)In common: scikit-learn, pandas, Matplotlib, 1 other tool, clinical / translational, 1 reference
- [9] doi:10.1162/imag.a.1259 [code]
- Neuronal avalanches as a predictive biomarker for guiding tailored BCI training programs.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Pingouin, scikit-learn, pandas, 2 other tools, clinical / translational
- [10] doi:10.1371/journal.pbio.3003856 [code]
- Aging and metabolism contribute separately to brain-body health.Journal: PLoS biologyIn common: scikit-learn, pandas, Matplotlib, 1 other tool, clinical / translational, 1 reference
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 10 scripts, and 14 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:190c70cb8a551ae7…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
