|
from typing import Optional, List |
|
import argparse |
|
import json |
|
import glob |
|
from pathlib import Path |
|
from datetime import datetime |
|
|
|
|
|
def get_latest_log() -> str: |
|
"""Find the most recently modified log file in the current directory. |
|
|
|
Returns: |
|
str: Path to the most recently modified log file |
|
|
|
Raises: |
|
FileNotFoundError: If no log files are found in the current directory |
|
""" |
|
logs = list(Path(".").glob("api_usage_*.json")) |
|
if not logs: |
|
raise FileNotFoundError("No log files found in the current directory.") |
|
return str(max(logs, key=lambda p: p.stat().st_mtime)) |
|
|
|
|
|
def format_cost(entry: dict) -> str: |
|
"""Format cost if available, otherwise return 'N/A' |
|
|
|
Args: |
|
entry: Log entry dictionary containing cost information |
|
|
|
Returns: |
|
str: Formatted cost string with $ and 4 decimal places, or 'N/A' if cost not found |
|
""" |
|
return f"${entry.get('cost', 'N/A'):.4f}" if "cost" in entry else "N/A" |
|
|
|
|
|
def print_gpt4_entry(entry: dict) -> None: |
|
"""Print entry for GPT-4 format |
|
|
|
Args: |
|
entry: Log entry dictionary in GPT-4 format containing model info, inputs and outputs |
|
""" |
|
print("\n=== Log Entry ===") |
|
print(f"Model: {entry['model']}") |
|
print(f"Case ID: {entry['case_id']}") |
|
print(f"Question ID: {entry['question_id']}") |
|
|
|
print("\n=== Model Input ===") |
|
messages = entry["input"]["messages"] |
|
print("System message:", messages[0]["content"]) |
|
user_content = messages[1]["content"] |
|
print("\nUser prompt:", user_content[0]["text"]) |
|
print("\nImages provided:") |
|
for content in user_content[1:]: |
|
print(f" - {content['image_url']['url']}") |
|
|
|
print("\n=== Model Output ===") |
|
print(f"Answer: {entry['model_answer']}") |
|
print(f"Correct: {entry['correct_answer']}") |
|
|
|
print("\n=== Usage Stats ===") |
|
print(f"Duration: {entry['duration']}s") |
|
print(f"Cost: {format_cost(entry)}") |
|
print( |
|
f"Tokens: {entry['usage']['total_tokens']}", |
|
f"(prompt: {entry['usage']['prompt_tokens']},", |
|
f"completion: {entry['usage']['completion_tokens']})", |
|
) |
|
|
|
|
|
def print_llama_entry(entry: dict) -> None: |
|
"""Print entry for Llama-3.2 format |
|
|
|
Args: |
|
entry: Log entry dictionary in Llama format containing model info, inputs and outputs |
|
""" |
|
print("\n=== Log Entry ===") |
|
print(f"Model: {entry['model']}") |
|
print(f"Case ID: {entry['case_id']}") |
|
print(f"Question ID: {entry['question_id']}") |
|
|
|
print("\n=== Model Input ===") |
|
print(f"Question: {entry['input']['question_data']['question']}") |
|
print("\nImages provided:") |
|
for url in entry["input"]["image_urls"]: |
|
print(f" - {url}") |
|
if entry["input"]["image_captions"]: |
|
print("\nImage captions:") |
|
for caption in entry["input"]["image_captions"]: |
|
if caption: |
|
print(f" - {caption}") |
|
|
|
print("\n=== Model Output ===") |
|
print(f"Answer: {entry['model_answer']}") |
|
print(f"Correct: {entry['correct_answer']}") |
|
|
|
print("\n=== Usage Stats ===") |
|
print(f"Duration: {entry['duration']}s") |
|
if "usage" in entry: |
|
print( |
|
f"Tokens: {entry['usage']['total_tokens']}", |
|
f"(prompt: {entry['usage']['prompt_tokens']},", |
|
f"completion: {entry['usage']['completion_tokens']})", |
|
) |
|
|
|
|
|
def determine_model_type(entry: dict) -> str: |
|
"""Determine the model type from the entry |
|
|
|
Args: |
|
entry: Log entry dictionary containing model information |
|
|
|
Returns: |
|
str: Model type - 'gpt4', 'llama', or 'unknown' |
|
""" |
|
model = entry.get("model", "").lower() |
|
if "gpt-4" in model: |
|
return "gpt4" |
|
elif "llama" in model: |
|
return "llama" |
|
elif "chexagent" in model: |
|
return "chexagent" |
|
elif "medrax" in model: |
|
return "medrax" |
|
else: |
|
return "unknown" |
|
|
|
|
|
def print_log_entry( |
|
log_file: Optional[str] = None, |
|
num_entries: Optional[int] = None, |
|
model_filter: Optional[str] = None, |
|
) -> None: |
|
"""Print log entries from the specified log file or the latest log file. |
|
|
|
Args: |
|
log_file: Path to the log file. If None, uses the latest log file. |
|
num_entries: Number of entries to print. If None, prints all entries. |
|
model_filter: Filter entries by model type ('gpt4' or 'llama'). If None, prints all. |
|
""" |
|
if log_file is None: |
|
log_file = get_latest_log() |
|
print(f"Using latest log file: {log_file}") |
|
|
|
entries_printed = 0 |
|
total_entries = 0 |
|
filtered_entries = 0 |
|
|
|
with open(log_file, "r") as f: |
|
for line in f: |
|
if line.startswith("HTTP"): |
|
continue |
|
try: |
|
total_entries += 1 |
|
entry = json.loads(line) |
|
|
|
|
|
model_type = determine_model_type(entry) |
|
if model_filter and model_type != model_filter: |
|
filtered_entries += 1 |
|
continue |
|
|
|
if model_type == "gpt4": |
|
print_gpt4_entry(entry) |
|
elif model_type == "llama": |
|
print_llama_entry(entry) |
|
else: |
|
print(f"Unknown model type in entry: {entry['model']}") |
|
continue |
|
|
|
print("=" * 50) |
|
entries_printed += 1 |
|
if num_entries and entries_printed >= num_entries: |
|
break |
|
|
|
except (json.JSONDecodeError, KeyError) as e: |
|
print(f"Error processing entry: {e}") |
|
continue |
|
|
|
print(f"\nSummary:") |
|
print(f"Total entries: {total_entries}") |
|
print(f"Entries printed: {entries_printed}") |
|
if model_filter: |
|
print(f"Entries filtered: {filtered_entries}") |
|
|
|
|
|
def main() -> None: |
|
"""Main entry point for the script""" |
|
parser = argparse.ArgumentParser( |
|
description="Parse and display log entries from API usage logs." |
|
) |
|
parser.add_argument("-l", "--log_file", nargs="?", help="Path to the log file (optional)") |
|
parser.add_argument("-n", "--num_entries", type=int, help="Number of entries to display") |
|
parser.add_argument( |
|
"-m", |
|
"--model", |
|
choices=["gpt4", "llama"], |
|
default="gpt4", |
|
help="Model type to display (default: gpt4)", |
|
) |
|
args = parser.parse_args() |
|
|
|
try: |
|
print_log_entry(args.log_file, args.num_entries, args.model) |
|
except FileNotFoundError as e: |
|
print(f"Error: {e}") |
|
exit(1) |
|
|
|
|
|
if __name__ == "__main__": |
|
main() |
|
|