Spaces:
Sleeping
Sleeping
# suppress warnings | |
import warnings | |
warnings.filterwarnings("ignore") | |
# import libraries | |
import argparse | |
from together import Together | |
import textwrap | |
import os | |
from datetime import datetime | |
import gradio as gr | |
# Parse command line arguments | |
parser = argparse.ArgumentParser(description="AI Study Tool") | |
parser.add_argument( | |
"--together_api_key", | |
"-k", | |
default="9806a2601560024637df1e4acd804862faa67e08637db6598d920b64eebba43e", | |
help="Together AI API key", | |
) | |
args = parser.parse_args() | |
# Initialize client with API key | |
client = Together(api_key=args.together_api_key) | |
# Add the cats content as a constant string | |
CATS_CONTENT = """The cat (Felis catus), also referred to as the domestic cat or house cat, is a small domesticated carnivorous mammal. It is the only domesticated species of the family Felidae. Advances in archaeology and genetics have shown that the domestication of the cat occurred in the Near East around 7500 BC. It is commonly kept as a pet and working cat, but also ranges freely as a feral cat avoiding human contact. It is valued by humans for companionship and its ability to kill vermin. Its retractable claws are adapted to killing small prey species such as mice and rats. It has a strong, flexible body, quick reflexes, and sharp teeth, and its night vision and sense of smell are well developed. It is a social species, but a solitary hunter and a crepuscular predator. | |
Cat intelligence is evident in their ability to adapt, learn through observation, and solve problems. Research has shown they possess strong memories, exhibit neuroplasticity, and display cognitive skills comparable to those of a young child. Cat communication includes meowing, purring, trilling, hissing, growling, grunting, and body language. It can hear sounds too faint or too high in frequency for human ears, such as those made by small mammals. It secretes and perceives pheromones. | |
Female domestic cats can have kittens from spring to late autumn in temperate zones and throughout the year in equatorial regions, with litter sizes often ranging from two to five kittens. Domestic cats are bred and shown at cat fancy events as registered pedigreed cats. Population control includes spaying and neutering, but pet abandonment has exploded the global feral cat population, which has driven the extinction of bird, mammal, and reptile species. | |
Domestic cats are found across the globe, though their popularity as pets varies by region. Out of the estimated 600 million cats worldwide, 400 million reside in Asia, including 58 million pet cats in China. The United States leads in cat ownership with 73.8 million cats. In the United Kingdom, approximately 10.9 million domestic cats are kept as pets. | |
Etymology and naming | |
The origin of the English word cat, Old English catt, is thought to be the Late Latin word cattus, which was first used at the beginning of the 6th century.[4] The Late Latin word may be derived from an unidentified African language.[5] The Nubian word kaddîska (wildcat) and Nobiin kadīs are possible sources or cognates.[6] | |
The forms might also have derived from an ancient Germanic word that was absorbed into Latin and then into Greek, Syriac, and Arabic.[7] The word may be derived from Germanic and Northern European languages, and ultimately be borrowed from Uralic, cf. Northern Sámi gáđfi, female stoat, and Hungarian hölgy, lady, female stoat; from Proto-Uralic *käďwä, female (of a furred animal).[8] | |
The English puss, extended as pussy and pussycat, is attested from the 16th century and may have been introduced from Dutch poes or from Low German puuskatte, related to Swedish kattepus, or Norwegian pus, pusekatt. Similar forms exist in Lithuanian puižė and Irish puisín or puiscín. The etymology is unknown, but it may be an onomatopoeia from using a sound to attract a cat.[9][10] | |
A male cat is called a tom or tomcat[11] (or a gib,[12] if neutered). A female is called a queen.[13] Some sources write that queen refers solely to unspayed cats that are in an estrous cycle.[14] (or sometimes a molly,[15] if spayed). A juvenile cat is referred to as a kitten, a term interchangeable with the now-obsolete word catling in Early Modern English.[16] A group of cats can be referred to as a clowder, a glaring,[17] or a colony.[18] | |
Taxonomy | |
The scientific name Felis catus was proposed by Carl Linnaeus in 1758 for a domestic cat.[1][2] Felis catus domesticus was proposed by Johann Christian Polycarp Erxleben in 1777.[3] Felis daemon proposed by Konstantin Satunin in 1904 was a black cat from the Transcaucasus, later identified as a domestic cat.[19][20] | |
In 2003, the International Commission on Zoological Nomenclature ruled that the domestic cat is a distinct species, namely Felis catus.[21][22] In 2007, the modern domesticated subspecies F. silvestris catus sampled worldwide was considered to have probably descended from the African wildcat (F. lybica), following results of phylogenetic research.[23][24][a] In 2017, the IUCN Cat Classification Taskforce followed the recommendation of the ICZN in regarding the domestic cat as a distinct species, Felis catus.[25]""" | |
## FUNCTION 1: This Allows Us to Prompt the AI MODEL | |
# ------------------------------------------------- | |
def prompt_llm(prompt, with_linebreak=False): | |
# This function allows us to prompt an LLM via the Together API | |
# model | |
model = "meta-llama/Meta-Llama-3-8B-Instruct-Lite" | |
# Make the API call | |
response = client.chat.completions.create( | |
model=model, | |
messages=[{"role": "user", "content": prompt}], | |
) | |
output = response.choices[0].message.content | |
if with_linebreak: | |
# Wrap the output | |
wrapped_output = textwrap.fill(output, width=50) | |
return wrapped_output | |
else: | |
return output | |
## FUNCTION 2: Load text file content | |
# ------------------------------------------------- | |
def load_file(filepath): | |
"""Load content from a text file""" | |
try: | |
with open(filepath, "r", encoding="utf-8") as file: | |
return file.read() | |
except FileNotFoundError: | |
print(f"Error: File {filepath} not found.") | |
return None | |
except Exception as e: | |
print(f"Error reading file: {e}") | |
return None | |
## FUNCTION 3: Generate bite-sized concept summaries | |
# ------------------------------------------------- | |
def summarize_concepts(text_content): | |
"""Generate bite-sized 2-sentence concepts from text content""" | |
prompt = f""" | |
Please analyze the following text and break it down into bite-sized learning concepts. | |
For each concept, provide exactly 2 sentences that capture the key idea in a clear, | |
easy-to-understand way suitable for studying. | |
Format each concept as: | |
Concept X: [2 sentences explaining the concept] | |
Text to analyze: | |
{text_content} | |
""" | |
return prompt_llm(prompt) | |
## FUNCTION 4: Save results to timestamped file | |
# ------------------------------------------------- | |
def save_results(content, output_dir="results"): | |
"""Save content to a timestamped file in the results directory""" | |
# Create results directory if it doesn't exist | |
os.makedirs(output_dir, exist_ok=True) | |
# Generate timestamp | |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
filename = f"{timestamp}.txt" | |
filepath = os.path.join(output_dir, filename) | |
# Save content | |
try: | |
with open(filepath, "w", encoding="utf-8") as file: | |
file.write(content) | |
print(f"Results saved to: {filepath}") | |
return filepath | |
except Exception as e: | |
print(f"Error saving file: {e}") | |
return None | |
## FUNCTION 5: Create Gradio Interface | |
# ------------------------------------------------- | |
def create_gradio_interface(): | |
"""Create a Gradio interface for the study tool""" | |
def process_file_and_generate_content(): | |
"""Process the cats content and generate study content""" | |
try: | |
# Use the hardcoded content instead of loading from file | |
content = CATS_CONTENT | |
# Generate summaries | |
summaries = summarize_concepts(content) | |
# Parse summaries into individual concepts - improved parsing for **Concept X:** format | |
concepts = [] | |
# Split by **Concept and filter out empty parts | |
parts = summaries.split("**Concept") | |
for i, part in enumerate(parts): | |
if i == 0: # Skip the intro text before first concept | |
continue | |
# Clean up the part and add back "Concept" prefix | |
concept_text = "Concept" + part.strip() | |
# Remove the closing ** if present | |
if concept_text.endswith("**"): | |
concept_text = concept_text[:-2].strip() | |
# Remove any ** formatting within the text | |
concept_text = concept_text.replace("**", "") | |
if concept_text: | |
concepts.append(concept_text) | |
# Save results | |
save_results(f"BITE-SIZED CONCEPT SUMMARIES:\n{summaries}") | |
# Return first concept for immediate display | |
first_concept = concepts[0] if concepts else "No concepts generated." | |
return ( | |
f"Content processed successfully! Found {len(concepts)} concepts. Navigate through them below.", | |
summaries, | |
concepts, | |
0, # current concept index | |
first_concept, # display first concept immediately | |
) | |
except Exception as e: | |
return f"Error processing content: {str(e)}", "", [], 0, "Error occurred." | |
def navigate_concept(concepts, current_idx, direction): | |
"""Navigate through concepts (previous/next)""" | |
if not concepts: | |
return "No concepts available.", 0 | |
if direction == "next": | |
new_idx = min(current_idx + 1, len(concepts) - 1) | |
else: # previous | |
new_idx = max(current_idx - 1, 0) | |
concept_text = ( | |
concepts[new_idx] if new_idx < len(concepts) else "No concept available." | |
) | |
return concept_text, new_idx | |
# Create Gradio interface | |
with gr.Blocks(title="AI Study Tool", theme=gr.themes.Soft()) as interface: | |
gr.Markdown("# 📚 AI Study Tool") | |
gr.Markdown("Generate bite-sized concepts from cat information for studying!") | |
# Process button section | |
with gr.Row(): | |
process_btn = gr.Button("Process Cat Content", variant="primary") | |
# Status and results | |
status_output = gr.Textbox(label="Status", interactive=False) | |
# Hidden state variables | |
full_summaries = gr.State("") | |
concepts_list = gr.State([]) | |
current_concept_idx = gr.State(0) | |
# Concept navigation section | |
with gr.Row(): | |
gr.Markdown("## 🧠 Study Concepts") | |
with gr.Row(): | |
prev_btn = gr.Button("← Previous Concept") | |
concept_counter = gr.Textbox( | |
label="Concept Position", interactive=False, value="0 / 0" | |
) | |
next_btn = gr.Button("Next Concept →") | |
concept_display = gr.Textbox( | |
label="Current Concept", | |
lines=4, | |
interactive=False, | |
placeholder="Process a file to see concepts here...", | |
) | |
# Event handlers | |
process_btn.click( | |
fn=process_file_and_generate_content, | |
inputs=[], | |
outputs=[ | |
status_output, | |
full_summaries, | |
concepts_list, | |
current_concept_idx, | |
concept_display, | |
], | |
).then( | |
fn=lambda concepts, idx: ( | |
f"{idx + 1} / {len(concepts)}" if concepts else "0 / 0" | |
), | |
inputs=[concepts_list, current_concept_idx], | |
outputs=[concept_counter], | |
) | |
prev_btn.click( | |
fn=lambda concepts, idx: navigate_concept(concepts, idx, "previous"), | |
inputs=[concepts_list, current_concept_idx], | |
outputs=[concept_display, current_concept_idx], | |
).then( | |
fn=lambda concepts, idx: ( | |
f"{idx + 1} / {len(concepts)}" if concepts else "0 / 0" | |
), | |
inputs=[concepts_list, current_concept_idx], | |
outputs=[concept_counter], | |
) | |
next_btn.click( | |
fn=lambda concepts, idx: navigate_concept(concepts, idx, "next"), | |
inputs=[concepts_list, current_concept_idx], | |
outputs=[concept_display, current_concept_idx], | |
).then( | |
fn=lambda concepts, idx: ( | |
f"{idx + 1} / {len(concepts)}" if concepts else "0 / 0" | |
), | |
inputs=[concepts_list, current_concept_idx], | |
outputs=[concept_counter], | |
) | |
return interface | |
if __name__ == "__main__": | |
# Launch Gradio interface | |
interface = create_gradio_interface() | |
interface.launch() | |