zach commited on
Commit
3ce989d
·
1 Parent(s): d4052d1

Add gradio UI code, and anthropic integration

Browse files
Files changed (2) hide show
  1. anthropic_api.py +135 -0
  2. app.py +95 -0
anthropic_api.py CHANGED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ anthropic_api.py
3
+
4
+ This file defines the interaction with the Anthropic API, focusing on generating text
5
+ using the Claude model. It includes functionality for input validation, API request handling,
6
+ and processing API responses.
7
+
8
+ Key Features:
9
+ - Encapsulates all logic related to the Anthropic API.
10
+ - Implements retry logic for handling transient API errors.
11
+ - Validates the response content to ensure API compatibility.
12
+ - Provides detailed logging for debugging and error tracking.
13
+
14
+ Classes:
15
+ - AnthropicError: Custom exception for Anthropic API-related errors.
16
+ - SystemPrompt: Frozen dataclass for storing the system prompt, ensuring immutability.
17
+
18
+ Functions:
19
+ - generate_text_with_claude: Generates text using the Anthropic SDK with input validation and retry logic.
20
+ """
21
+
22
+ # Standard Library Imports
23
+ from dataclasses import dataclass
24
+ from typing import Union, List
25
+ # Third-Party Library Imports
26
+ from anthropic import Anthropic
27
+ from anthropic.types import Message, ModelParam, TextBlock
28
+ from tenacity import retry, stop_after_attempt, wait_fixed
29
+ # Local Application Imports
30
+ from config import logger
31
+ from utils import truncate_text, validate_env_var
32
+
33
+
34
+ @dataclass(frozen=True)
35
+ class AnthropicConfig:
36
+ """Immutable configuration for interacting with the Anthropic API."""
37
+ model: ModelParam = "claude-3-5-sonnet-latest" # Valid predefined model
38
+ max_tokens: int = 300 # Max tokens for API response
39
+ system_prompt: str = """You are a highly creative and articulate assistant specialized in generating vivid, engaging, and well-written content.
40
+
41
+ Your task is to respond to user prompts by creating:
42
+ 1. Short stories,
43
+ 2. Poems,
44
+ 3. Or other creative written outputs.
45
+
46
+ Ensure that your responses are:
47
+ - Imaginative and original,
48
+ - Coherent and well-structured,
49
+ - Suitable for a wide audience, avoiding controversial or sensitive topics.
50
+
51
+ When writing, tailor your tone and style to match the user's request. For example:
52
+ - If the user requests a poem, provide creative and rhythmic verse.
53
+ - If the user requests a short story, ensure a clear beginning, middle, and end with compelling details.
54
+
55
+ Always keep your responses concise, unless explicitly instructed to elaborate."""
56
+
57
+ class AnthropicError(Exception):
58
+ """Custom exception for errors related to the Anthropic API."""
59
+ def __init__(self, message: str, original_exception: Exception = None):
60
+ super().__init__(message)
61
+ self.original_exception = original_exception
62
+
63
+
64
+ # Initialize the Anthropic client
65
+ api_key: str = validate_env_var("ANTHROPIC_API_KEY")
66
+ client: Anthropic = Anthropic(api_key=api_key)
67
+ anthropic_config = AnthropicConfig()
68
+
69
+
70
+ @retry(stop=stop_after_attempt(3), wait=wait_fixed(2))
71
+ def generate_text_with_claude(prompt: str) -> str:
72
+ """
73
+ Generates text using Claude via the Anthropic SDK.
74
+
75
+ Args:
76
+ prompt (str): The input prompt for Claude.
77
+
78
+ Returns:
79
+ str: The generated text.
80
+
81
+ Raises:
82
+ ValueError: If the prompt exceeds the maximum allowed length.
83
+ AnthropicError: If there is an error communicating with the Anthropic API.
84
+
85
+ Example:
86
+ >>> generate_text_with_claude("Write a haiku about nature.")
87
+ "Gentle waves crashing, / Whispering secrets softly, / Infinite blue skies."
88
+
89
+ >>> generate_text_with_claude("")
90
+ "The prompt exceeds the maximum allowed length of 500 characters. Your prompt contains 512 characters."
91
+ """
92
+ # Log model, max tokens, and system prompt for debugging
93
+ logger.debug(f"Using model: {anthropic_config.model}, max tokens: {anthropic_config.max_tokens}")
94
+ logger.debug(f"System prompt: {truncate_text(anthropic_config.system_prompt)}")
95
+ logger.debug(f"Preparing API request with prompt: {prompt[:50]}{'...' if len(prompt) > 50 else ''}")
96
+
97
+ try:
98
+ response: Message = client.messages.create(
99
+ model=anthropic_config.model,
100
+ max_tokens=anthropic_config.max_tokens,
101
+ system=anthropic_config.system_prompt,
102
+ messages=[{"role": "user", "content": prompt}],
103
+ )
104
+ logger.debug(f"API response received: {truncate_text(str(response))}")
105
+
106
+ # Validate response content
107
+ if not hasattr(response, "content"):
108
+ logger.error("Response is missing 'content'. Response: %s", response)
109
+ raise AnthropicError("Invalid API response: Missing 'content'.")
110
+
111
+ # Process response content
112
+ blocks: Union[List[TextBlock], TextBlock, None] = response.content
113
+
114
+ if isinstance(blocks, list):
115
+ result = "\n\n".join(block.text for block in blocks if isinstance(block, TextBlock))
116
+ logger.debug(f"Processed response from list: {truncate_text(result)}")
117
+ return result
118
+ if isinstance(blocks, TextBlock):
119
+ logger.debug(f"Processed response from single TextBlock: {truncate_text(blocks.text)}")
120
+ return blocks.text
121
+
122
+ logger.warning(f"Unexpected response type: {type(blocks)}")
123
+ return str(blocks or "No content generated.")
124
+
125
+ except Exception as e:
126
+ logger.exception(f"Error generating text with Claude: {e}")
127
+ raise AnthropicError(
128
+ message=(
129
+ f"Error generating text with Claude: {e}. "
130
+ f"HTTP Status: {getattr(response, 'status', 'N/A')}. "
131
+ f"Prompt (truncated): {truncate_text(prompt)}. "
132
+ f"Model: {anthropic_config.model}, Max tokens: {anthropic_config.max_tokens}"
133
+ ),
134
+ original_exception=e,
135
+ )
app.py CHANGED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ app.py
3
+
4
+ This file defines the Gradio user interface for interacting with the Anthropic API.
5
+ Users can input prompts, which are processed and passed to the Claude model via the API.
6
+ The generated responses are displayed back to the user in the Gradio UI.
7
+
8
+ Key Features:
9
+ - Gradio interface for user interaction.
10
+ - Input validation via prompt length constraints.
11
+ - Logging of user interactions and API responses.
12
+
13
+ Functions:
14
+ - process_prompt: Handles user input, calls the API, and returns generated text.
15
+ - build_gradio_interface: Constructs the Gradio Blocks-based interface.
16
+ """
17
+
18
+ # Third-Party Library Imports
19
+ import gradio as gr
20
+ # Local Application Imports
21
+ from anthropic_api import generate_text_with_claude
22
+ from config import logger
23
+ from utils import truncate_text, validate_prompt_length
24
+
25
+
26
+ # Constants
27
+ PROMPT_MIN_LENGTH: int = 10
28
+ PROMPT_MAX_LENGTH: int = 500
29
+
30
+
31
+ def process_prompt(prompt: str) -> str:
32
+ """
33
+ Process the user prompt and generate text using the Claude API.
34
+
35
+ Args:
36
+ prompt (str): The user's input prompt.
37
+
38
+ Returns:
39
+ str: The generated text or an error message.
40
+ """
41
+ logger.debug(f"Entering process_prompt with prompt: {prompt}")
42
+ try:
43
+ # Validate prompt length before processing
44
+ validate_prompt_length(prompt, PROMPT_MAX_LENGTH, PROMPT_MIN_LENGTH)
45
+ generated_text = generate_text_with_claude(prompt)
46
+ logger.debug(f"Generated text: {generated_text}")
47
+ logger.info("Successfully generated text.")
48
+ return generated_text
49
+ except ValueError as ve:
50
+ logger.warning(f"Validation error: {ve}")
51
+ return str(ve) # Return validation error directly to the UI
52
+ except Exception as e:
53
+ logger.error(f"Unexpected error generating text: {e}")
54
+ return "An unexpected error occurred. Please try again."
55
+
56
+
57
+ def build_gradio_interface() -> gr.Blocks:
58
+ """
59
+ Build the Gradio user interface.
60
+
61
+ Returns:
62
+ gr.Blocks: The Gradio Blocks object representing the interface.
63
+ """
64
+ with gr.Blocks() as demo:
65
+ gr.Markdown("# TTS Arena")
66
+ gr.Markdown("Generate text from a prompt using **Claude by Anthropic**.")
67
+
68
+ with gr.Row():
69
+ prompt_input = gr.Textbox(
70
+ label="Enter your prompt",
71
+ placeholder=f"Prompt Claude to generate a poem or short story...",
72
+ lines=2,
73
+ )
74
+
75
+ with gr.Row():
76
+ generate_button = gr.Button("Generate")
77
+
78
+ with gr.Row():
79
+ output_text = gr.Textbox(label="Generated Text", interactive=False, lines=10)
80
+
81
+ # Attach the validation and processing logic
82
+ generate_button.click(
83
+ fn=process_prompt,
84
+ inputs=prompt_input,
85
+ outputs=output_text,
86
+ )
87
+
88
+ logger.debug("Gradio interface built successfully")
89
+ return demo
90
+
91
+
92
+ if __name__ == "__main__":
93
+ logger.info("Launching TTS Arena Gradio app...")
94
+ demo = build_gradio_interface()
95
+ demo.launch()