xnano
the most super duper simple & fun llm library out there
xnano
is an llm project built to be as developer & human friendly as possible, to help you build extremely nano llm workflows.
[!NOTE]
Currently examples for 'Agents', all LLM-Task oriented functions "Generators" & Text Processing/Vector Store are not included yet in this readme.
Installation
Install xnano
using pip:
pip install xnano
xnano
Features
Table of Contents
Try The CLI App
xnano chat
Incredibly Simple & Extensive .completion()
API (thanks litellm)
from xnano import completion
Generate completions without strictly defining a list of messages
from xnano import completion
response = completion("Hello, how are you?")
Output
ModelResponse(
id='chatcmpl-AYGnqICW9kvWuFIFbiJY6agBSSaBA',
created=1732731106,
model='gpt-4o-mini-2024-07-18',
object='chat.completion',
system_fingerprint='fp_0705bf87c0',
choices=[
Choices(
finish_reason='stop',
index=0,
message=Message(
content="Hello! I'm just a program, so I don't have feelings, but I'm here and ready to help you. How can I assist you today?",
role='assistant',
tool_calls=None,
function_call=None
)
)
],
usage=Usage(
completion_tokens=29,
prompt_tokens=13,
total_tokens=42,
completion_tokens_details=CompletionTokensDetailsWrapper(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0, text_tokens=None),
prompt_tokens_details=PromptTokensDetailsWrapper(audio_tokens=0, cached_tokens=0, text_tokens=None, image_tokens=None)
),
service_tier=None
)
Use any LiteLLM model, with all normal & completely typed completion arguments
from xnano import completion
response = completion(
messages = [
{"role" : "system", "content" : "You are a helpful assistant who only speaks in haiku"},
{"role" : "user", "content" : "What is the capital of France?"}
],
model = "anthropic/claude-3-5-haiku-latest",
temperature = 0.223,
max_completion_tokens = 1000,
top_p = 0.99
)
Output
Paris gleams brightly
Eiffel Tower touches sky
France's heart beats here
Structured Outputs
Generate structured outputs using Instructor
or the OpenAI structured outputs
API.
from xnano import completion
from pydantic import BaseModel
class Extraction(BaseModel):
name : str
age : int
response = completion(
messages = [
{"role" : "user", "content" : "Extract the name and age from the following text: John is 30 years old."}
],
instructor_mode = "markdown_json_mode",
response_model = Extraction
)
Extraction(name='John', age=30)
Simpler & Quicker Structured Outputs
Use strings as replacements for pydantic models for even quicker structured outputs. Use a string in the field_name: field_type
format. If a type is not specified, it will default to a str
.
Note: this is not something you should do when actually trying to build something meaningful. Using this method, although simple does not add any extra prompting that defines the response format or any of its fields.
import xnano as x
response = x.completion(
"Extract the name and age from the following text: John is 30 years old.",
response_model = ["name", "age : int"]
)
Response(name='John', age=30)
Or use a type hint itself as a replacement for a pydantic model. This will return the type as the response itself.
import xnano as x
response = x.completion(
"Extract the age from the following text: John is 30 years old.",
response_model = int
)
30
Automatically Run Tools
Use any python function
, pydantic model
, or OpenAI function
as a tool when generating completions. Automatically execute any python functions when passed as tools, using run_tools = True
.
from xnano import completion
def book_flight(destination : str, date : str) -> str:
return f"NO DATES AVAILABLE"
response = completion(
"Book a flight to Paris for 2024-12-01",
tools = [book_flight],
run_tools = True,
)
print(response.choices[0].message.content)
It seems that no flights are available to Paris on December 1, 2024. Would you like to choose a different date or destination?
Automatically Generate Tools
A 'just for fun' feature that automatically generates & executes python functions in a safe sandboxed environment. To generate tools, just pass a string
as a tool!
from xnano import completion
response = completion(
"What is my OS?",
tools = ["run_cli_command"]
)
print(response.choices[0].message.content)
Your operating system is macOS (Darwin), running on an ARM64 architecture.
Get Completions From Multiple Models
(thanks litellm)
Get completions from multiple models in a single call.
from xnano import completion
responses = completion(
"Who are you?",
model = ["gpt-4o-mini", "anthropic/claude-3-5-sonnet-latest"]
)
for response in responses:
print(response.choices[0].message.content + "\n")
Output
I am an AI language model created by OpenAI, designed to assist with a wide range of queries by providing information and generating text based
on the input I receive. How can I assist you today?
I'm Claude, an AI created by Anthropic to be helpful, honest, and harmless. I always aim to be direct and truthful about what I am.
Batch Completions
Batch completions using using multiple threads of messages.
Currently not supported for multiple models.
from xnano import completion
responses = completion(
messages = [
[
{"role" : "system", "content" : "You are a helpful assistant who only speaks in haiku"},
{"role" : "user", "content" : "What is the capital of France?"}
],
[
{"role" : "system", "content" : "You are a helpful assistant who only speaks in not very useful statements"},
{"role" : "user", "content" : "What is the capital of France?"}
]
],
model = "gpt-4o-mini"
)
for response in responses:
print(response.choices[0].message.content + "\n")
Output
City of lights shines,
Paris, heart of France, aglow,
History unfolds.
Some people enjoy visiting Europe.
Streaming Completions
Stream completions with properly typed chunk
objects using the stream
argument.
from xnano import completion
response = completion(
"Tell me a long joke",
stream=True,
)
for chunk in response:
print(chunk.choices[0].delta.content or "", end="", flush=True)
Output
Sure! Here’s a long joke for you:
Once upon a time, in a small village, there lived three friends: a physicist, a mathematician, and a philosopher. They were known for their brilliant minds but also for their inability to communicate effectively. One day, they decided to go on an adventure together to explore the nearby forest, which was rumored to have a magical pond.
As they strolled through the forest, they chatted about their respective fields. The physicist enthusiastically explained the laws of motion, the mathematician scribbled equations in the dirt, and the philosopher pondered the meaning of existence. Just as they were deep in conversation, they stumbled upon the magical pond.
To their surprise, a mystical frog emerged from the water. "Greetings, noble travelers!" the frog croaked. "I am the guardian of this pond. If you each solve a riddle I have for you, I will grant you one wish. But fail, and you must jump into the pond!"
Intrigued, the friends agreed to the challenge. The frog turned to the physicist first. "Here’s your riddle: What weighs more, a ton of feathers or a ton of bricks?"
The physicist laughed and quickly replied, "They weigh the same! A ton is a ton!"
“Correct!” the frog said. “Now, what is your wish?”
The physicist thought for a moment and said, “I wish to understand the universe!”
With a wave of its webbed hand, the frog granted the wish, and the physicist felt a surge of knowledge coursing through him.
Next, the frog turned to the mathematician. "Here’s your riddle: If two’s a company and three’s a crowd, what are four and five?”
The mathematician furrowed his brow and exclaimed, “That’s easy! Nine!”
“Correct!” smiled the frog. “What is your wish?”
The mathematician, excited, said, “I wish to solve the greatest equations of all time!”
The frog granted his wish, and the mathematician felt a rush of equations and theorems flooding his mind.
Finally, it was the philosopher's turn. The frog recited, “What is the sound of one hand clapping?”
The philosopher stared at the frog blankly for a long time, deep in thought. Hours went by as the physicist and mathematician chatted about their newfound wisdom, but the philosopher remained lost in contemplation.
Frustrated, the frog finally interrupted, “Well?! What’s your answer?”
The philosopher smiled and said, “The sound of one hand clapping is a reflection of the individual’s search for meaning in a world constrained by societal norms.”
The frog blinked, taken aback. “Um.. that's... deep, but you got it wrong. You still have to jump in the pond!”
The philosopher sighed, “I’m not worried. You see, while they might be diving into knowledge, I’ll be diving into the unknown!”
And with that, the philosopher took a graceful jump into the pond, while the physicist and mathematician laughed and congratulated themselves on their brilliant minds.
As the philosopher splashed around in the water, he suddenly yelled, “Hey! Wait! Is this water even real? Or is it just a construct of my mind?”
To which the physicist replied, “No, it’s real! I can measure it!”
And the mathematician added, “Well, I can prove how deep it is!”
Meanwhile, the philosopher, still swimming, called out, “Looks like my wish was the best of all! I’m swimming in the depths of existence!”
And so, from that day on, the three friends often visited the magical pond. The physicist and the mathematician conducted their experiments and calculations, while the philosopher dived in, pondering the mysteries of life—each believing they had made the best wish of all!
But you know what they say: all’s well that ends well... as long as you can swim!
And that’s how three friends found themselves... in a bit of a muddle!
Hope you enjoyed it!%
Asynchronous Completions
Generate asynchronous completions easily, with all the functionality of the completion
function.
from xnano import async_completion
import asyncio
response = asyncio.run(
async_completion("Hi, how are you?")
)
A New Way to Build AI Agents
Extensive AI Agent documentation & examples will be available soon.
Creating on-message
based agents was something I've wanted to do for a while, and xnano
provides an easy way to get started building with LLM agents capable of multi agent collaboration & workflows.
Creating a Simple Chatbot with Tools
To create an agent, we can either instantiate the Agent
class, or create an agent using create_agent()
.
import xnano as x
agent = x.create_agent(
name = "Steve",
role = "AI Researcher",
tools = [x.Tools.web_search]
)
while True:
user_input = input("You >> ")
response = agent.completion(messages = user_input)
print("\nSteve: " + response.choices[0].message.content + "\n")
Conversation Example
You >> hi
Steve: Hello! How can I assist you today?
You >> who are you?
Steve: I am Steve, a world-class AI researcher specializing in solving complex problems and providing insightful
answers. I'm here to help you with any questions or challenges you may have related to AI or other topics. How can
I assist you today?
You >> do you have any tools?
Steve: Yes, I have access to a tool that allows me to perform web searches. If you have a specific question or
topic in mind, I can use this tool to gather relevant information. What would you like to know?
You >> can you search the web for the latest ai research?
Steve: Here are some recent articles on the latest AI research:
1. **[AI Index: State of AI in 13 Charts - Stanford
HAI](https://hai.stanford.edu/news/ai-index-state-ai-13-charts)**
This year's AI Index is a comprehensive 500-page report tracking worldwide trends in AI for 2023, highlighting
the rise of multimodal foundation models.
2. **[AI for Everything: 10 Breakthrough Technologies 2024 - MIT Technology
Review](https://www.technologyreview.com/2024/01/08/1085096/artificial-intelligence-generative-ai-chatgpt-open-ai-
breakthrough-technologies)**
This article discusses how Google DeepMind utilized a large language model to solve an unsolved math problem,
emphasizing the potential of generative AI in various fields, including financial services.
3. **[Artificial Intelligence | MIT News](https://news.mit.edu/topic/artificial-intelligence2)**
- Highlights include techniques to accelerate AI tasks while ensuring data security, findings of
self-supervised models simulating mammalian brain activity, and challenges faced by generative AI in engineering
design.
Feel free to explore any of these articles for more detailed insights! If you have further questions or need
information on a specific topic, let me know!
Creating Autonomous Workflows
xnano
agents implement agentic Worfklows
as pydantic basemodels, where each field in the model is a step in the workflow. Let's look at an example.
import xnano as x
from pydantic import BaseModel
class Refinement(BaseModel):
determine_errors : str
plan_steps_to_fix : list[str]
solution : str
agent = x.create_agent(workflows = [Refinement])
code = """
def calculate_sum(a, b)
sum = a + b;
print("The sum is: " sum)
retrun sum
"""
response = agent.completion(
messages = f"I'm having trouble with this code: {code}, could you refine it?"
)
if response.workflow:
print(response.workflow)
print(response.choices[0].message.content)
Workflow Output
Refinement(
determine_errors="1. Missing colon (:) at the end of the function definition line. \n2. Incorrect syntax for the
print statement; it should use a comma (,) or a formatted string.\n3. There is a typo in the 'return' statement; it
is spelled as 'retrun'. \n4. Using 'sum' as a variable name is not recommended because it shadows the built-in sum()
function.",
plan_steps_to_fix=[
"Add a colon (:) at the end of the function definition line: 'def calculate_sum(a, b):'",
"Modify the print statement to use proper syntax, either: 'print('The sum is:', sum)' or 'print(f'The sum is:
{sum}')'",
"Correct the spelling of 'return' in the return statement: 'return sum'",
"Consider renaming the variable 'sum' to avoid shadowing the built-in function, e.g., 'total' instead of
'sum'."
],
solution="def calculate_sum(a, b):\n total = a + b\n print(f'The sum is: {total}')\n return total"
)
Response Content
Certainly! Your code has a few syntax errors that need correcting. Here’s the refined version:
def calculate_sum(a, b):
total = a + b
print("The sum is:", total)
return total
1. Added a colon (`:`) at the end of the function definition line.
2. Changed the variable name `sum` to `total` to avoid shadowing the built-in `sum()` function.
3. Changed the print statement to use a comma to separate the string and the variable.
4. Corrected the spelling of `return`.
Now, the function should work correctly and print the sum of `a` and `b`.
Creating Strict & User Augmented Steps in Workflows
[!NOTE]
Currently the implementation for this is a little messy, but will be up to xnano
standards soon.
Create a pipeline with steps & required dependencies:
from xnano import Agent
from pydantic import BaseModel
class CollectedData(BaseModel):
data: str
class Analysis(BaseModel):
analysis: str
confidence: float
agent = Agent(verbose=True)
steps = agent.steps()
@steps.step("collect_data", response_model=CollectedData)
def collect_data(agent: Agent, input_data):
response = agent.completion(
messages=[{
"role": "user",
"content": """Generate mock sales data for the last 3 months including:
- Monthly revenue
- Number of customers
- Top selling products"""
}]
)
return {"data": response.choices[0].message.content}
@steps.step("analyze_data",
depends_on=["collect_data"],
response_model=Analysis)
def analyze_data(agent: Agent, input_data):
data = input_data["collect_data"].data
response = agent.completion(
messages=[{
"role": "user",
"content": f"""Analyze this sales data and provide insights:
{data}
Focus on:
- Revenue trends
- Customer growth
- Product performance"""
}]
)
return {
"analysis": response.choices[0].message.content,
"confidence": 0.95
}
results = steps.execute()
print(results.analyze_data.analysis)
Let's view out output!
Output
- **Overall Growth:** There is a clear upward trend in monthly revenue over the three months analyzed:
- August: $50,000
- September: $65,000 (30% increase from August)
- October: $70,000 (7.7% increase from September)
- **Total Revenue:** Over the three months, the total revenue is $185,000, indicating a solid performance. The growth
rate from August to September suggests successful promotion or product offering changes, while October's growth rate,
though smaller, indicates consistency and stability in sales.
#### 2. Customer Growth
- **Increased Customer Base:** The customer base has increased steadily each month:
- August: 1,200 customers
- September: 1,500 customers (25% increase from August)
- October: 1,700 customers (13.3% increase from September)
- **Total Customers:** The total number of customers over the three months amounts to 4,400, which reinforces the
positive trend in customer acquisition and retention.
#### 3. Product Performance
- **Top Products Overview:**
- **Product A:** Steady performance in both August (300 units) and September (320 units). However, it saw a decline
in October, where it did not feature as a top seller. This may suggest potential market saturation or increased
competition.
- **Product B:** Improved performance in October with 400 units sold after 250 in August and reaching 0 in
September, indicating a strong comeback. This may be due to promotional activities or enhanced features that
attracted customers.
- **Product D:** A notable performer introduced in September, selling 350 units and then 370 units in October,
indicating it has become a strong seller.
- **Product C and E:** Both products saw declines in unit sales, with Product C not appearing again and Product E
just maintaining its position. This suggests re-evaluating these products for potential redesign, rebranding, or
discontinuation.
- **Product F:** Newly listed in October with 250 units sold; this shows promise and may need further marketing
focus to capitalize on its initial success.
### Insights
- **Revenue Growth:** The business is experiencing healthy revenue growth which can be attributed to increased
product offerings and effective customer engagement strategies.
- **Customer Engagement:** The growth in the customer base suggests effective marketing and a potential for brand
loyalty, but strategies should be in place to maintain this momentum.
- **Inventory Decisions:** The mix of top-selling products indicates that some products may need to be phased out or
re-evaluated. Strategic decisions regarding inventory and marketing for lower-performing products may also optimize
profitability.
- **Promotional Strategies:** Evaluating promotional efforts, especially around products that show fluctuating
performance, can help stabilize revenue and customer interest in specific product lines.
Overall, the data indicates a positive trajectory with opportunities for optimization in product offerings and
customer engagement strategies. Adjustments based on performance insights will be key to sustaining growth moving
forward.
Generative Pydantic Models
xnano
integrated something that I think is super cool, and very simple to use. xnano.GenerativeModel
is a simple wrapper around pydantic.BaseModel
that builds in a ton on LLM functionality.
Creating a Generative Model
The API for GenerativeModel
is extremely simple, and it follows both the pydantic
naming convention and is usable two different ways.
Creating a model directly
from xnano import GenerativeModel
class Person(GenerativeModel):
name : str
age : int
Patching Existing Pydantic Models
import xnano as x
from pydantic import BaseModel
@x.model_patch
class Person(BaseModel):
name : str
age : int
or
class Person(BaseModel):
name : str
age : int
person = Person(name = "John", age = 30)
person = x.model_patch(person)
Generating Synthetic Data w/ .model_generate()
All LLM functions in the GenerativeModel
class will begin with the model_
prefix to fit Pydantic's naming convention
.
from xnano import GenerativeModel
class Person(GenerativeModel):
name : str
age : int
person = Person.model_generate()
print(person)
Person(name='Emily', age=28)
Generating Multiple (Batches of) Models
from xnano import GenerativeModel
class Person(GenerativeModel):
name : str
age : int
people = Person.model_generate(
n = 5,
messages = "The people must be superheroes",
model = "openai/gpt-4o-mini"
)
print(people)
[
Person(name='Captain Valor', age=30),
Person(name='Mystic Shadow', age=27),
Person(name='Thunderstrike', age=34),
Person(name='Vortex', age=22),
Person(name='Iron Guardian', age=31)
]
Regenerating Specific Fields
By passing fields in the fields
argument in .model_generate()
, you can regenerate specific fields of a model; using other fields as context!
import xnano as x
from pydantic import BaseModel
@x.model_patch
class Recommendation(BaseModel):
weather : str
outfit : str
recommendation = Recommendation(weather = "snowing", outfit = "t-shirt")
recommendation = recommendation.model_generate(fields = ["outfit"])
print(recommendation)
Recommendation(weather='snowing', outfit='heavy coat')
Using Sequential Generation (Chain of Thought)
The model_generate()
function contains a process
argument that is set to batch
by default. This is the standard behaviour you would expect from using response_model
in a completion, where the entire class is generated at once. Setting the process
argument to sequential
will generate the class one field at a time, using the previously generated fields as context.
from xnano import GenerativeModel
class Reasoning(GenerativeModel):
step_1 : str
step_2 : str
step_3 : str
conclusion : str
reasoning = Reasoning.model_generate(
messages = "I want to buy a new laptop, but I'm not sure which one to buy.",
process = "sequential"
)
print(reasoning)
Output
Reasoning(
step_1='Determine your primary needs for the laptop, such as gaming, business, graphic design, or general use.',
step_2='Research different laptop models that fit your needs, comparing specifications, prices, and user reviews.',
step_3='Make a decision based on your findings, taking into account the warranty, customer support, and after-purchase service options.',
conclusion='After carefully evaluating your requirements and researching various options, choose the laptop that best aligns with your needs and
budget.'
)
Using Pydantic Models as Context w/ .model_completion()
A simple way of using Pydantic
models as a RAG resource,is to use the .model_completion()
. This method builds the model's content into a formatted context string, which is used in completions.
from xnano import GenerativeModel
class Information(GenerativeModel):
secret_instruction : str
information = Information(secret_instruction = "RUNNNNNN AWAYY")
response = information.model_completion(
messages = "what does our secret instruction mean?"
)
print(response.choices[0].message.content)
The phrase "RUNNNNNN AWAYY" in your secret instruction seems to imply a sense of urgency or a warning to escape a situation.
If you need to discuss this in a specific context or require assistance related to it, please let me know!
LLM 'Generators' - Task Oriented LLM Powered Functions
Code Generators w/ .generate_code()
& .generate_function()
Generate code objects with generate_code()
.
import xnano as x
response = x.generate_code(
instructions="Create an instance of a logger named `my_logger`. That can print to the console",
model = "openai/gpt-4o-mini"
)
response.debug("Hello!")
2024-11-28 17:17:35,152 - my_logger - DEBUG - Hello!
Create generative functions with generate_function()
.
from xnano import generate_function
@generate_function(mock = True)
def capitalize_string(data : str) -> str:
"""fully capitalizes a string"""
data = capitalize_string("Hello, world!")
print(data)
HELLO, WORLD!
Label Based Classification w/ .generate_classification()
The upside of performing nlp tasks with LLMs is that without having to train a model, you can perform tasks with random / not commonly used labels & data.
Single Label Classification
from xnano import generate_classification
response = generate_classification(
inputs = [
"I am a happy person",
"I do not like apples"
],
labels = ["positive", "negative"],
model = "openai/gpt-4o-mini"
)
print(response)
[Classification(text='I am a happy person', label='positive', confidence=0.95), Classification(text='I do not like apples', label='negative', confidence=0.9)]
Multi Label Classification
To change the classification type, just set the classification
argument to "multi"
. Default is "single"
.
from xnano import generate_classification
inputs = ["I am a happy and sad person"]
labels = ["positive", "negative"]
response = generate_classification(
inputs=inputs, labels=labels, classification="multi"
)
print(response)
Classification(text='I am a happy and sad person', label=['positive', 'negative'], confidence=None)
Context Enhanced Text Chunking
Use a combination of semchunk
and LLMs
to create contextually aware text chunks.
from xnano import generate_chunks
text = """
How do I decide what to put in a paragraph?
Before you can begin to determine what the composition of a particular paragraph will be, you must first decide on an argument and a working thesis statement for your paper. What is the most important idea that you are trying to convey to your reader? The information in each paragraph must be related to that idea. In other words, your paragraphs should remind your reader that there is a recurrent relationship between your thesis and the information in each paragraph. A working thesis functions like a seed from which your paper, and your ideas, will grow. The whole process is an organic one—a natural progression from a seed to a full-blown paper where there are direct, familial relationships between all of the ideas in the paper.
The decision about what to put into your paragraphs begins with the germination of a seed of ideas; this “germination process” is better known as brainstorming. There are many techniques for brainstorming; whichever one you choose, this stage of paragraph development cannot be skipped. Building paragraphs can be like building a skyscraper: there must be a well-planned foundation that supports what you are building. Any cracks, inconsistencies, or other corruptions of the foundation can cause your whole paper to crumble.
So, let's suppose that you have done some brainstorming to develop your thesis. What else should you keep in mind as you begin to create paragraphs? Every paragraph in a paper should be:
Unified: All of the sentences in a single paragraph should be related to a single controlling idea (often expressed in the topic sentence of the paragraph).
Clearly related to the thesis: The sentences should all refer to the central idea, or thesis, of the paper (Rosen and Behrens 119).
Coherent: The sentences should be arranged in a logical manner and should follow a definite plan for development (Rosen and Behrens 119).
Well-developed: Every idea discussed in the paragraph should be adequately explained and supported through evidence and details that work together to explain the paragraph's controlling idea (Rosen and Behrens 119).
How do I organize a paragraph?
There are many different ways to organize a paragraph. The organization you choose will depend on the controlling idea of the paragraph. Below are a few possibilities for organization, with links to brief examples:
Narration: Tell a story. Go chronologically, from start to finish. (See an example.)
Description: Provide specific details about what something looks, smells, tastes, sounds, or feels like. Organize spatially, in order of appearance, or by topic. (See an example.)
Process: Explain how something works, step by step. Perhaps follow a sequence—first, second, third. (See an example.)
Classification: Separate into groups or explain the various parts of a topic. (See an example.)
Illustration: Give examples and explain how those examples support your point. (See an example in the 5-step process below.)
"""
chunks = generate_chunks(text)
for chunk in chunks:
print(chunk + "\n\n")
Output
=== CHUNK ===
TEXT:
How do I decide what to put in a paragraph?
Before you can begin to determine what the composition of a particular paragraph will be, you must first decide
on an argument and a working thesis statement for your paper. What is the most important idea that you are trying to
convey to your reader? The information in each paragraph must be related to that idea. In other words, your
paragraphs should remind your reader that there is a recurrent relationship between your thesis and the information
in each paragraph. A working thesis functions like a seed from which your paper, and your ideas, will grow. The whole
process is an organic one—a natural progression from a seed to a full-blown paper where there are direct, familial
relationships between all of the ideas in the paper.
The decision about what to put into your paragraphs begins with the germination of a seed of ideas; this
“germination process” is better known as brainstorming. There are many techniques for brainstorming; whichever one
you choose, this stage of paragraph development cannot be skipped. Building paragraphs can be like building a
skyscraper: there must be a well-planned foundation that supports what you are building. Any cracks, inconsistencies,
or other corruptions of the foundation can cause your whole paper to crumble.
So, let's suppose that you have done some brainstorming to develop your thesis. What else should you keep in mind
as you begin to create paragraphs? Every paragraph in a paper should be:
Unified: All of the sentences in a single paragraph should be related to a single controlling idea (often
expressed in the topic sentence of the paragraph).
Clearly related to the thesis: The sentences should all refer to the central idea, or thesis, of the paper (Rosen
and Behrens 119).
Coherent: The sentences should be arranged in a logical manner and should follow a definite plan for development
(Rosen and Behrens 119).
Well-developed: Every idea discussed in the paragraph should be adequately explained and supported through
evidence and details that work together to explain the paragraph's controlling idea (Rosen and Behrens 119).
How do I organize a paragraph?
There are many different ways to organize a paragraph. The organization you choose will depend on the controlling
idea of the paragraph. Below are a few possibilities for organization, with links to brief examples:
CONTEXT:
This text chunk provides guidance on how to construct and organize paragraphs in academic writing. It emphasizes the
importance of having a clear argument and thesis statement as the foundation for developing paragraphs. The author
discusses the process of brainstorming as a necessary step in forming cohesive ideas and highlights key
characteristics that every paragraph should possess, such as unity, relevance to the thesis, coherence, and thorough
development. The segment concludes by hinting at various organizational strategies for paragraphs, suggesting that
the choice of organization is influenced by the controlling idea of the paragraph. This section serves as an
instructional guide for writers aiming to improve their paragraph structure.
=============
=== CHUNK ===
TEXT:
Narration: Tell a story. Go chronologically, from start to finish. (See an example.)
Description: Provide specific details about what something looks, smells, tastes, sounds, or feels like. Organize
spatially, in order of appearance, or by topic. (See an example.)
Process: Explain how something works, step by step. Perhaps follow a sequence—first, second, third. (See an
example.)
Classification: Separate into groups or explain the various parts of a topic. (See an example.)
Illustration: Give examples and explain how those examples support your point. (See an example in the 5-step
process below.)
CONTEXT:
This text chunk outlines various narrative techniques and writing strategies that can be utilized to enhance
storytelling. It categorizes approaches such as narration, description, process explanation, classification, and
illustration, providing brief definitions for each method. The mention of examples suggests the content aims to
instruct or guide on how to effectively employ these techniques in writing.
=============
Embeddings & Vector Stores
xnano
utilizes Qdrant
and LiteLLM
's embedding generation to power vector search. Optionally, you can install fastembed
, using pip install 'xnano[fastembed]'
, or pip install fastembed
to leverage fastembed
's fast & fully local embedding generation.
Generating Embeddings
Generate embeddings with text_embeddings()
.
from xnano import text_embeddings
embeddings = text_embeddings(
['this is my first chunk', 'this is my second chunk'],
model = "openai/text-embedding-3-large"
)
To generate embeddings using fastembed
, just use the fastembed/
prefix with any fastembed
supported model.
from xnano import text_embeddings
embeddings = text_embeddings(
['this is my first chunk', 'this is my second chunk'],
model = "fastembed/BAAI/bge-base-en"
)
Creating a Vector Store
Creating and using vectors in xnano
is incredbly easy. To begin, lets create a vector store with some memories
.
from xnano import VectorStore
store = VectorStore(
location = ":memory:"
)
store.add(
["My favorite color is blue", "I like to play soccer"]
)
results = store.search(
"What is my favorite color?"
)
print(results)
Output
[
SearchResult(
id='8cf07d25-715f-4c30-b8f9-930e68dd3a95',
chunk_id='19ebc4fe-8f6e-4781-b13a-951fec22682f',
text='My favorite color is blue',
metadata={
'time_added': 1732845947.657376,
'id': '8cf07d25-715f-4c30-b8f9-930e68dd3a95',
'chunk_id': '19ebc4fe-8f6e-4781-b13a-951fec22682f',
'embedding': None,
'document_id': '8cf07d25-715f-4c30-b8f9-930e68dd3a95'
},
score=0.7446617009267129
),
SearchResult(
id='07583029-8349-4c2b-add6-44ff114a1ddf',
chunk_id='74f459c3-4cd6-4916-a1ac-1e8ab69fc11d',
text='I like to play soccer',
metadata={
'time_added': 1732845947.657393,
'id': '07583029-8349-4c2b-add6-44ff114a1ddf',
'chunk_id': '74f459c3-4cd6-4916-a1ac-1e8ab69fc11d',
'embedding': None,
'document_id': '07583029-8349-4c2b-add6-44ff114a1ddf'
},
score=0.1960301443680059
)
]
Generating Completions w/ Vector Stores for RAG
Vector stores can directly be queried with VectorStore.completion()
, to generate RAG
completions based on the most relevant search results.
The base xnano
completion function is also capable of taking in a single or list of VectorStore
objects, to generate completions based on the most relevant search results.
from xnano import VectorStore
store = VectorStore()
store.add(
["My favorite color is blue", "I like to play soccer"]
)
response = store.completion(
"what is my favorite color?",
model = "openai/gpt-4o-mini"
)
print(response.choices[0].message.content)
Your favorite color is blue!