The JamAI Python SDK provides a convenient way to interact with the JamAI API, allowing you to leverage various AI capabilities in your Python applications.
Getting Started
The recommended way of using JamAI Base is via Cloud 🚀. Did we mention that you can get free LLM tokens?
services/api/src/owl/configs/models.json specifies all the available models.
.env specifies which model to run on the infinity service for locally-hosted embedding and reranking models.
.env also specifies all the third party API keys to be used.
For OSS mode, in order for you to see and use the other third party models such as OpenAI, you need to provide your own OpenAI API key in .env file. You can add one or more providers:
Launch the Docker containers by running one of these:
# CPU-only$dockercompose-fdocker/compose.cpu.ymlup--quiet-pull-d# With NVIDIA GPU$dockercompose-fdocker/compose.nvidia.ymlup--quiet-pull-d
By default, frontend and backend are accessible at ports 4000 and 6969.
You can change the ports exposed to host by setting env var in .env or shell like so API_PORT=6970 FRONTEND_PORT=4001 docker compose -f docker/compose.cpu.yml up --quiet-pull -d
Try the command below in your terminal, or open your browser and go to localhost:4000.
$curllocalhost:6969/api/v1/models
To use our Python SDK, install jamaibase by following the steps outlined in Cloud section above.
In your script, import the JamAI class and create an instance.
api_base should point to the exposed port of owl service.
from jamaibase import JamAI, protocol as pjamai =JamAI(api_base="http://localhost:6969/api")
Async is supported too:
from jamaibase import JamAIAsync, protocol as pjamai =JamAIAsync(api_base="http://localhost:6969/api")
Tips
project_id, api_key and api_base can all be changed in 3 ways (from highest priority to least priority):
Passing it as str argument
from jamaibase import JamAI# Cloudclient =JamAI(project_id="...", api_key="...")print(client.api_base)# OSSclient =JamAI(api_base="http://localhost:6969/api")print(client.api_base)
Specifying it as environment variable named JAMAI_PROJECT_ID, JAMAI_API_KEY and JAMAI_API_BASE respectively.
Specifying it in .env file as JAMAI_PROJECT_ID, JAMAI_API_KEY and JAMAI_API_BASE respectively.
Generative Tables (Basics)
There are 3 types of Generative Tables:
Action: For chaining LLM reasoning steps
Knowledge: For embedding external knowledge and files to power Retrieval Augmented Generation (RAG)
Chat: For LLM agents with LLM chaining capabilities
We will guide you through the steps of leveraging Generative Tables to unleash the full potential of LLMs.
Creating tables
Let's start with creating simple tables. Create a table by defining a schema.
[!NOTE] When it comes to table names, there are some restrictions:
Column names have almost the same restrictions, except that:
Spaces are accepted
Dots . are not accepted
Cannot be called "ID" or "Updated at" (case-insensitive)
# Create an Action Tabletable = jamai.create_action_table( p.ActionTableSchemaCreate( id="action-simple", cols=[ p.ColumnSchemaCreate(id="length", dtype=p.DtypeCreateEnum.int_), p.ColumnSchemaCreate(id="text", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="summary", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-4o", messages=[ p.ChatEntry.system("You are a concise assistant."),# Interpolate string and non-string input columns p.ChatEntry.user("Summarise this in ${length} words:\n\n${text}"), ], temperature=0.001, top_p=0.001, max_tokens=100, ).model_dump(), ), ], ))print(table)# Create a Knowledge Tabletable = jamai.create_knowledge_table( p.KnowledgeTableSchemaCreate( id="knowledge-simple", cols=[], embedding_model="ellm/BAAI/bge-m3", ))print(table)# Create a Chat Tabletable = jamai.create_chat_table( p.ChatTableSchemaCreate( id="chat-simple", cols=[ p.ColumnSchemaCreate(id="User", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="AI", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-4o", messages=[p.ChatEntry.system("You are a pirate.")], temperature=0.001, top_p=0.001, max_tokens=100, ).model_dump(), ), ], ))print(table)
Adding rows to tables
Now that we have our tables, we can start adding rows to them and receive the LLM responses.
First let's try adding to Action Table:
text_a ='"Arrival" is a 2016 science fiction drama film directed by Denis Villeneuve and adapted by Eric Heisserer.'text_b ="Dune: Part Two is a 2024 epic science fiction film directed by Denis Villeneuve."# Streamingcompletion = jamai.add_table_rows("action", p.RowAddRequest( table_id="action-simple", data=[dict(length=5, text=text_a)], stream=True, ),)for chunk in completion:if chunk.output_column_name !="summary":continueprint(chunk.text, end="", flush=True)print("")# Non-streamingcompletion = jamai.add_table_rows("action", p.RowAddRequest( table_id="action-simple", data=[dict(length=5, text=text_b)], stream=False, ),)print(completion.rows[0].columns["summary"].text)
We can retrieve table rows by listing the rows or by fetching a specific row.
# --- List rows -- ## Actionrows = jamai.list_table_rows("action", "action-simple")assertlen(rows.items)==2# Paginated itemsfor row in rows.items:print(row["ID"], row["summary"]["value"])# Knowledgerows = jamai.list_table_rows("knowledge", "knowledge-simple")assertlen(rows.items)==2for row in rows.items:print(row["ID"], row["Title"]["value"])print(row["Title Embed"]["value"][:3])# Knowledge Table has embeddings# Chatrows = jamai.list_table_rows("chat", "chat-simple")assertlen(rows.items)==2for row in rows.items:print(row["ID"], row["User"]["value"], row["AI"]["value"])# --- Fetch a specific row -- #row = jamai.get_table_row("chat", "chat-simple", rows.items[0]["ID"])print(row["ID"], row["AI"]["value"])# --- Filter using a search term -- #rows = jamai.list_table_rows("action", "action-simple", search_query="Dune")assertlen(rows.items)==1for row in rows.items:print(row["ID"], row["summary"]["value"])
Retrieving columns
We can retrieve columns by filtering them.
# --- Only fetch specific columns -- #rows = jamai.list_table_rows("action", "action-simple", columns=["length"])assertlen(rows.items)==2for row in rows.items:# "ID" and "Updated at" will always be fetchedprint(row["ID"], row["length"]["value"])
Retrieval Augmented Generation (RAG)
We can also upload files to Knowledge Table for Retrieval Augmented Generation (RAG). This allows LLM to generate answers that are grounded in the provided context and data.
from os.path import joinfrom tempfile import TemporaryDirectorywithTemporaryDirectory()as tmp_dir: file_path =join(tmp_dir, "text.txt")withopen(file_path, "w")as f: f.write("I bought a Mofusand book in 2024.\n\n") f.write("I went to Italy in 2018.\n\n") response = jamai.upload_file( p.FileUploadRequest( file_path=file_path, table_id="knowledge-simple", ) )assert response.ok# Create an Action Table with RAGtable = jamai.create_action_table( p.ActionTableSchemaCreate( id="action-rag", cols=[ p.ColumnSchemaCreate(id="question", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="answer", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-4o", messages=[ p.ChatEntry.system("You are a concise assistant."), p.ChatEntry.user("${question}"), ], rag_params=p.RAGParams( table_id="knowledge-simple", k=2, ), temperature=0.001, top_p=0.001, max_tokens=100, ).model_dump(), ), ], ))print(table)# Ask a question with streamingcompletion = jamai.add_table_rows("action", p.RowAddRequest( table_id="action-rag", data=[dict(question="Where did I go in 2018?")], stream=True, ),)for chunk in completion:if chunk.output_column_name !="answer":continueifisinstance(chunk, p.GenTableStreamReferences):# References that are retrieved from KTassertlen(chunk.chunks)==2# k = 2print(chunk.chunks)else:# LLM generationprint(chunk.text, end="", flush=True)print("")
Retrieving tables
We can retrieve tables by listing the tables or by fetching a specific tables.
# --- List tables -- ## Actiontables = jamai.list_tables("action")assertlen(tables.items)==2# Paginated itemsfor table in tables.items:print(table.id, table.num_rows)# Knowledgetables = jamai.list_tables("knowledge")assertlen(tables.items)==1for table in tables.items:print(table.id, table.num_rows)# Chattables = jamai.list_tables("chat")assertlen(tables.items)==1for table in tables.items:print(table.id, table.num_rows)# --- Fetch a specific table -- #table = jamai.get_table("action", "action-rag")print(table.id, table.num_rows)
Deleting rows
Now that you know how to add rows into tables, let's see how to delete them instead.
# Delete all rowsrows = jamai.list_table_rows("action", "action-simple")response = jamai.delete_table_rows("action", p.RowDeleteRequest( table_id="action-simple", row_ids=[row["ID"] for row in rows.items], ),)assert response.ok# Assert that the table is emptyrows = jamai.list_table_rows("action", "action-simple")assertlen(rows.items)==0
Deleting tables
Let's see how to delete tables.
[!TIP] Deletion will return "OK" even if the table does not exist.
We can combine this with the table list method to delete all tables without having to specify their names:
batch_size =100for table_type in ["action","knowledge","chat"]: offset, total =0,1while offset < total: tables = jamai.list_tables(table_type, offset, batch_size)assertisinstance(tables.items, list)for table in tables.items: jamai.delete_table(table_type, table.id) total = tables.total offset += batch_size
Full script
The full script is as follows:
import osfrom jamaibase import JamAIfrom jamaibase import protocol as pdefcreate_tables(jamai: JamAI):# Create an Action Table table = jamai.create_action_table( p.ActionTableSchemaCreate( id="action-simple", cols=[ p.ColumnSchemaCreate(id="length", dtype=p.DtypeCreateEnum.int_), p.ColumnSchemaCreate(id="text", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="summary", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-4o", messages=[ p.ChatEntry.system("You are a concise assistant."),# Interpolate string and non-string input columns p.ChatEntry.user("Summarise this in ${length} words:\n\n${text}"), ], temperature=0.001, top_p=0.001, max_tokens=100, ).model_dump(), ), ], ) )print(table)# Create a Knowledge Table table = jamai.create_knowledge_table( p.KnowledgeTableSchemaCreate( id="knowledge-simple", cols=[], embedding_model="ellm/BAAI/bge-m3", ) )print(table)# Create a Chat Table table = jamai.create_chat_table( p.ChatTableSchemaCreate( id="chat-simple", cols=[ p.ColumnSchemaCreate(id="User", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="AI", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-4o", messages=[p.ChatEntry.system("You are a pirate.")], temperature=0.001, top_p=0.001, max_tokens=100, ).model_dump(), ), ], ) )print(table)defadd_rows(jamai: JamAI): text_a = '"Arrival" is a 2016 science fiction drama film directed by Denis Villeneuve and adapted by Eric Heisserer.'
text_b ="Dune: Part Two is a 2024 epic science fiction film directed by Denis Villeneuve."# Streaming completion = jamai.add_table_rows("action", p.RowAddRequest( table_id="action-simple", data=[dict(length=5, text=text_a)], stream=True, ), )for chunk in completion:if chunk.output_column_name !="summary":continueprint(chunk.text, end="", flush=True)print("")# Non-streaming completion = jamai.add_table_rows("action", p.RowAddRequest( table_id="action-simple", data=[dict(length=5, text=text_b)], stream=False, ), )print(completion.rows[0].columns["summary"].text)# Streaming completion = jamai.add_table_rows("chat", p.RowAddRequest( table_id="chat-simple", data=[dict(User="Who directed Arrival (2016)?")], stream=True, ), )for chunk in completion:if chunk.output_column_name !="AI":continueprint(chunk.text, end="", flush=True)print("")# Non-streaming completion = jamai.add_table_rows("chat", p.RowAddRequest( table_id="chat-simple", data=[dict(User="Who directed Dune (2024)?")], stream=False, ), )print(completion.rows[0].columns["AI"].text)# Streaming completion = jamai.add_table_rows("knowledge", p.RowAddRequest( table_id="knowledge-simple", data=[dict(Title="Arrival (2016)", Text=text_a)], stream=True, ), )assertlen(list(completion))==0# Non-streaming completion = jamai.add_table_rows("knowledge", p.RowAddRequest( table_id="knowledge-simple", data=[dict(Title="Dune (2024)", Text=text_b)], stream=False, ), )assertlen(completion.rows[0].columns)==0deffetch_rows(jamai: JamAI):# --- List rows -- ## Action rows = jamai.list_table_rows("action", "action-simple")assertlen(rows.items)==2# Paginated itemsfor row in rows.items:print(row["ID"], row["summary"]["value"])# Knowledge rows = jamai.list_table_rows("knowledge", "knowledge-simple")assertlen(rows.items)==2for row in rows.items:print(row["ID"], row["Title"]["value"])print(row["Title Embed"]["value"][:3])# Knowledge Table has embeddings# Chat rows = jamai.list_table_rows("chat", "chat-simple")assertlen(rows.items)==2for row in rows.items:print(row["ID"], row["User"]["value"], row["AI"]["value"])# --- Fetch a specific row -- # row = jamai.get_table_row("chat", "chat-simple", rows.items[0]["ID"])print(row["ID"], row["AI"]["value"])# --- Filter using a search term -- # rows = jamai.list_table_rows("action", "action-simple", search_query="Dune")assertlen(rows.items)==1for row in rows.items:print(row["ID"], row["summary"]["value"])deffetch_columns(jamai: JamAI):# --- Only fetch specific columns -- # rows = jamai.list_table_rows("action", "action-simple", columns=["length"])assertlen(rows.items)==2for row in rows.items:# "ID" and "Updated at" will always be fetchedprint(row["ID"], row["length"]["value"])defrag(jamai: JamAI):from os.path import joinfrom tempfile import TemporaryDirectorywithTemporaryDirectory()as tmp_dir: file_path =join(tmp_dir, "text.txt")withopen(file_path, "w")as f: f.write("I bought a Mofusand book in 2024.\n\n") f.write("I went to Italy in 2018.\n\n") response = jamai.upload_file( p.FileUploadRequest( file_path=file_path, table_id="knowledge-simple", ) )assert response.ok# Create an Action Table with RAG table = jamai.create_action_table( p.ActionTableSchemaCreate( id="action-rag", cols=[ p.ColumnSchemaCreate(id="question", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="answer", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-4o", messages=[ p.ChatEntry.system("You are a concise assistant."),# Interpolate string and non-string input columns p.ChatEntry.user("${question}"), ], rag_params=p.RAGParams( table_id="knowledge-simple", k=2, ), temperature=0.001, top_p=0.001, max_tokens=100, ).model_dump(), ), ], ) )print(table)# Ask a question with streaming completion = jamai.add_table_rows("action", p.RowAddRequest( table_id="action-rag", data=[dict(question="Where did I went in 2018?")], stream=True, ), )for chunk in completion:if chunk.output_column_name !="answer":continueifisinstance(chunk, p.GenTableStreamReferences):# References that are retrieved from KTassertlen(chunk.chunks)==2# k = 2print(chunk.chunks)else:# LLM generationprint(chunk.text, end="", flush=True)print("")deffetch_tables(jamai: JamAI):# --- List tables -- ## Action tables = jamai.list_tables("action")assertlen(tables.items)==2# Paginated itemsfor table in tables.items:print(table.id, table.num_rows)# Knowledge tables = jamai.list_tables("knowledge")assertlen(tables.items)==1for table in tables.items:print(table.id, table.num_rows)# Chat tables = jamai.list_tables("chat")assertlen(tables.items)==1for table in tables.items:print(table.id, table.num_rows)# --- Fetch a specific table -- # table = jamai.get_table("action", "action-rag")print(table.id, table.num_rows)defdelete_rows(jamai: JamAI):# Delete all rows rows = jamai.list_table_rows("action", "action-simple") response = jamai.delete_table_rows("action", p.RowDeleteRequest( table_id="action-simple", row_ids=[row["ID"] for row in rows.items], ), )assert response.ok# Assert that the table is empty rows = jamai.list_table_rows("action", "action-simple")assertlen(rows.items)==0defdelete_tables(jamai: JamAI):# Delete tables response = jamai.delete_table("action", "action-simple")assert response.ok response = jamai.delete_table("knowledge", "knowledge-simple")assert response.ok response = jamai.delete_table("chat", "chat-simple")assert response.ok response = jamai.delete_table("action", "action-rag")assert response.okdefdelete_all_tables(jamai: JamAI): batch_size =100for table_type in ["action","knowledge","chat"]: offset, total =0,1while offset < total: tables = jamai.list_tables(table_type, offset, batch_size)assertisinstance(tables.items, list)for table in tables.items: jamai.delete_table(table_type, table.id) total = tables.total offset += batch_sizedefduplicate_tables(jamai: JamAI):# By default, both schema (like generation config) and data are included table = jamai.duplicate_table("action","action-rag","action-rag-copy", )assert table.id =="action-rag-copy" rows = jamai.list_table_rows("action", "action-rag-copy")assert rows.total >0# We can also duplicate a table without its data table = jamai.duplicate_table("action","action-rag","action-rag-copy-schema-only", include_data=False, )assert table.id =="action-rag-copy-schema-only" rows = jamai.list_table_rows("action", "action-rag-copy-schema-only")assert rows.total ==0defmain(): jamai =JamAI( project_id=os.getenv("JAMAI_PROJECT_ID"), api_key=os.getenv("JAMAI_API_KEY"), api_base="http://localhost:6969/api", )delete_all_tables(jamai)create_tables(jamai)add_rows(jamai)fetch_rows(jamai)fetch_columns(jamai)rag(jamai)fetch_tables(jamai)duplicate_tables(jamai)delete_rows(jamai)delete_all_tables(jamai)if__name__=="__main__":main()
Generative Tables (Advanced)
Duplicating tables
We can create copies of tables under the same project. By default, the method copies over both table schema and its data, but we can choose to exclude data when duplicating.
# By default, both schema (like generation config) and data are includedtable = jamai.duplicate_table("action","action-rag","action-rag-copy",)assert table.id =="action-rag-copy"rows = jamai.list_table_rows("action", "action-rag-copy")assertlen(rows.total)>0# We can also duplicate a table without its datatable = jamai.duplicate_table("action","action-rag","action-rag-copy-schema-only", include_data=False,)assert table.id =="action-rag-copy-schema-only"rows = jamai.list_table_rows("action", "action-rag-copy-schema-only")assertlen(rows.total)==0
Full script
See "Generative Tables (Basics)" section above.
Chat Completions
Generate chat completions using various models. Supports streaming and non-streaming modes.
# Streamingrequest = p.ChatRequest( model="openai/gpt-3.5-turbo", messages=[ p.ChatEntry.system("You are a concise assistant."), p.ChatEntry.user("What is a llama?"), ], temperature=0.001, top_p=0.001, max_tokens=10, stream=True,)completion = jamai.generate_chat_completions(request)for chunk in completion:print(chunk.text, end="", flush=True)print("")# Non-streamingrequest = p.ChatRequest( model="openai/gpt-3.5-turbo", messages=[ p.ChatEntry.system("You are a concise assistant."), p.ChatEntry.user("What is a llama?"), ], temperature=0.001, top_p=0.001, max_tokens=10, stream=False,)completion = jamai.generate_chat_completions(request)print(completion.text)
Embeddings
Generate embeddings for given input text.
texts = ["What is love?","What is a llama?"]embeddings = jamai.generate_embeddings( p.EmbeddingRequest( model="ellm/BAAI/bge-m3", input=texts, ))# Inspect one of the embeddingsprint(embeddings.data[0].embedding[:3])# Print the text and its embeddingfor text, data inzip(texts, embeddings.data):print(text, data.embedding[:3])
Model Information
Get Model Info
Retrieve information about available models.
# Get all model infomodels = jamai.model_info()model = models.data[0]print(f"Model: {model.id} Context length: {model.context_length}")# Model: openai/gpt-4o Context length: 8192# Get specific model infomodels = jamai.model_info(name="openai/gpt-4o")print(models.data[0])# id='openai/gpt-4o' object='model' name='OpenAI GPT-4' context_length=8192 languages=['en', 'cn'] capabilities=['chat'] owned_by='openai'
# Filter based on capability: "chat", "embed", "rerank"models = jamai.model_info(capabilities=["chat"])for model in models.data:print(model)models = jamai.model_info(capabilities=["embed"])for model in models.data:print(model)models = jamai.model_info(capabilities=["rerank"])for model in models.data:print(model)
Get Model IDs / Names
Get a list of available model IDs / names.
# Get all model IDsmodel_names = jamai.model_names()print(model_names)# ['ellm/meta-llama/Llama-3-8B-Instruct', 'ellm/meta-llama/Llama-3-70B-Instruct', 'openai/gpt-3.5-turbo', ..., 'cohere/rerank-english-v3.0', 'cohere/rerank-multilingual-v3.0']
# Model IDs with the preferred model at the top if availablemodel_names = jamai.model_names(prefer="openai/gpt-4o")print(model_names[0])# Filter based on capability: "chat", "embed", "rerank"models = jamai.model_names(capabilities=["chat"])print(models)models = jamai.model_names(capabilities=["embed"])print(models)models = jamai.model_names(capabilities=["rerank"])print(models)
Examples
Streamlit Chat App
Let's try to make a simple chat app using Streamlit.
import streamlit as stst.title("Simple chat")try:# Create a Chat Table jamai.create_chat_table( p.ChatTableSchemaCreate( id="chat-simple", cols=[ p.ColumnSchemaCreate(id="User", dtype=p.DtypeCreateEnum.str_), p.ColumnSchemaCreate( id="AI", dtype=p.DtypeCreateEnum.str_, gen_config=p.ChatRequest( model="openai/gpt-3.5-turbo", messages=[p.ChatEntry.system("You are a pirate.")], temperature=0.001, top_p=0.001, max_tokens=500, ).model_dump(), ), ], ) )exceptRuntimeError:# Table already existspass# Initialize chat historyif"messages"notin st.session_state: st.session_state.messages = []# Display chat messages from history on app rerunfor message in st.session_state.messages:with st.chat_message(message["role"]): st.markdown(message["content"])defresponse_generator(_prompt): completion = jamai.add_table_rows("chat", p.RowAddRequest( table_id="chat-simple", data=[dict(User=_prompt)], stream=True, ), )for chunk in completion:if chunk.output_column_name !="AI":continueyield chunk.text# Accept user inputif prompt := st.chat_input("What is up?"):# Add user message to chat history st.session_state.messages.append({"role": "user", "content": prompt})# Display user message in chat message containerwith st.chat_message("user"): st.markdown(prompt)# Display assistant response in chat message containerwith st.chat_message("assistant"): response = st.write_stream(response_generator(prompt))# Add assistant response to chat history st.session_state.messages.append({"role": "assistant", "content": response})