Skip to content

Commit

Permalink
New Release 2023.06.30 (#66)
Browse files Browse the repository at this point in the history
Watsonx Prompt Templates
Updated documentation and examples
Retry mechanism update
  • Loading branch information
vedem1192 committed Jun 30, 2023
1 parent a711a5f commit 44fea57
Show file tree
Hide file tree
Showing 56 changed files with 1,285 additions and 271 deletions.
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

This is the Python SDK for IBM Foundation Models Studio to bring IBM Generative AI into Python programs and to also extend it with useful operations and types.

:books: API Documentation: [Link](https://ibm.github.io/ibm-generative-ai/)
:books: **API Documentation: [Link](https://ibm.github.io/ibm-generative-ai/)**

*This is an early access library and requires invitation to use the technical preview of [watsonx.ai](https://watsonx.ai/). You can join the waitlist by visiting. https://www.ibm.com/products/watsonx-ai.*

Expand Down Expand Up @@ -101,11 +101,12 @@ creds = Credentials(api_key=my_api_key, api_endpoint=my_api_endpoint)
## <a name='Examples'></a>Examples

There are a number of examples you can try in the [`examples/user`](examples/user) directory.
Login to [workbench.res.ibm.com](https://workbench.res.ibm.com/) and get your GenAI API key. Then, create a `.env` file and assign the `GENAI_KEY` value as:
Login to [workbench.res.ibm.com](https://workbench.res.ibm.com/) and get your GenAI API key. Then, create a `.env` file and assign the `GENAI_KEY` value as below example. [More information](#gen-ai-endpoint)


```ini
GENAI_KEY=YOUR_GENAI_API_KEY
# GENAI_API=GENAI_API_ENDPOINT << for a different endpoint
```

### <a name='AsyncExample'></a>Async Example
Expand All @@ -116,7 +117,7 @@ import os
from dotenv import load_dotenv

from genai.model import Credentials, Model
from genai.schemas import GenerateParams, ModelType
from genai.schemas import GenerateParams

# make sure you have a .env file under genai root with
# GENAI_KEY=<your-genai-key>
Expand All @@ -142,7 +143,7 @@ params = GenerateParams(
# creds object
creds = Credentials(api_key, api_endpoint)
# model object
model = Model(ModelType.FLAN_UL2, params=params, credentials=creds)
model = Model("google/flan-ul2", params=params, credentials=creds)

greeting = "Hello! How are you?"
lots_of_greetings = [greeting] * 1000
Expand Down Expand Up @@ -170,7 +171,7 @@ import os
from dotenv import load_dotenv

from genai.model import Credentials, Model
from genai.schemas import GenerateParams, ModelType
from genai.schemas import GenerateParams

# make sure you have a .env file under genai root with
# GENAI_KEY=<your-genai-key>
Expand All @@ -196,7 +197,7 @@ params = GenerateParams(
# creds object
creds = Credentials(api_key, api_endpoint)
# model object
model = Model(ModelType.FLAN_UL2, params=params, credentials=creds)
model = Model("google/flan-ul2", params=params, credentials=creds)

greeting1 = "Hello! How are you?"
greeting2 = "I am fine and you?"
Expand Down Expand Up @@ -292,7 +293,7 @@ import os
from dotenv import load_dotenv
import genai.extensions.langchain
from genai.extensions.langchain import LangChainInterface
from genai.schemas import GenerateParams, ModelType
from genai.schemas import GenerateParams
from genai import Credentials, Model, PromptPattern

load_dotenv()
Expand All @@ -302,11 +303,11 @@ creds = Credentials(api_key, api_endpoint)
params = GenerateParams(decoding_method="greedy")

# As LangChain Model
langchain_model = LangChainInterface(model=ModelType.FLAN_UL2, params=params, credentials=creds)
langchain_model = LangChainInterface(model="google/flan-ul2", params=params, credentials=creds)
print(langchain_model("Answer this question: What is life?"))

# As GenAI Model
genai_model = Model(model=ModelType.FLAN_UL2, params=params, credentials=creds)
genai_model = Model(model="google/flan-ul2", params=params, credentials=creds)
print(genai_model.generate(["Answer this question: What is life?"])[0].generated_text)

# GenAI prompt pattern to langchain PromptTemplate and vice versa
Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/source/rst_source/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Examples

.. note::

|:tada:| All user examples `available on Github <https://github.com/IBM/ibm-generative-ai/tree/main/examples/user>`_ !
All user examples `available on Github <https://github.com/IBM/ibm-generative-ai/tree/main/examples/user>`_ !

.. toctree::
:maxdepth: 4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,80 +4,33 @@ Annexe

File : capital-qa.yaml
------------------------

.. code-block:: yaml
apiVersion: v0
name: captial-qa
content: |
Country: Germany
Capital: Berlin
Country: USA
Capital: Washington
Country: {{country}}
Capital:
.. literalinclude:: ../../../../examples/user/templates/capital-qa.yaml
:language: yaml
:caption: File is available at `capital-qa.yaml <https://github.com/IBM/ibm-generative-ai/blob/main/examples/user/templates/capital-qa.yaml>`_ on GitHub.

File : synth-animal.yaml
-------------------------

.. code-block:: yaml
apiVersion: v0
name: synth-animal
content: |
Please generate synthetic data about {{animal}}.
1,{{species1}},{{location1}},{{length1}},{{dob1}}
2,{{species2}},{{location2}},{{length2}},{{dob2}}
3,{{species3}},{{location3}},{{length3}},{{dob3}}
4,
.. literalinclude:: ../../../../examples/user/templates/synth-animal.yaml
:language: yaml
:caption: File is available at `synth-animal.yaml <https://github.com/IBM/ibm-generative-ai/blob/main/examples/user/templates/synth-animal.yaml>`_ on GitHub.

File : penguins.csv
---------------------

.. code-block:: text
"","species","island","bill_length_mm","bill_depth_mm","flipper_length_mm","body_mass_g","sex","year"
"1","Adelie","Torgersen",39.1,18.7,181,3750,"male",2007
"2","Adelie","Torgersen",39.5,17.4,186,3800,"female",2007
"3","Adelie","Torgersen",40.3,18,195,3250,"female",2007
"4","Adelie","Torgersen",NA,NA,NA,NA,NA,2007
"5","Gentoo","Biscoe",36.7,19.3,193,3450,"female",2007
"6","Gentoo","Biscoe",39.3,20.6,190,3650,"male",2007
...
"341","Chinstrap","Dream",43.5,18.1,202,3400,"female",2009
"342","Chinstrap","Dream",49.6,18.2,193,3775,"male",2009
"343","Chinstrap","Dream",50.8,19,210,4100,"male",2009
"344","Chinstrap","Dream",50.2,18.7,198,3775,"female",2009
.. literalinclude:: ../../../../examples/user/assets/penguins.csv
:language: text
:lines: 1-6,340-
:caption: File is available at `penguins.csv <https://github.com/IBM/ibm-generative-ai/blob/main/examples/user/assets/penguins.csv>`_ on GitHub.

File : instruction.yaml
-------------------------

.. code-block:: yaml
apiVersion: v0
name: instruction
content: |
1. Instruction: {{instruction1}}
1. Input: {{input1}}
1. Output: {{output1}}
2. Instruction: {{instruction2}}
2. Input: {{input2}}
2. Output: {{output2}}
3. Instruction:
.. literalinclude:: ../../../../examples/user/templates/instruction.yaml
:language: yaml
:lines: 2-
:caption: File is available at `instruction.yaml <https://github.com/IBM/ibm-generative-ai/blob/main/examples/user/templates/instruction.yaml>`_ on GitHub.

File : tasks.jsonl
-------------------------

.. code-block:: json
{"id": "seed_task_0", "name": "breakfast_suggestion", "instruction": "Is there anything I can eat for a breakfast that doesn't include eggs, yet includes protein, and has roughly 700-1000 calories?", "instances": [{"input": "", "output": "Yes, you can have 1 oatmeal banana protein shake and 4 strips of bacon. The oatmeal banana protein shake may contain 1/2 cup oatmeal, 60 grams whey protein powder, 1/2 medium banana, 1tbsp flaxseed oil and 1/2 cup watter, totalling about 550 calories. The 4 strips of bacon contains about 200 calories."}], "is_classification": false},
{"id": "seed_task_1", "name": "antonym_relation", "instruction": "What is the relation between the given pairs?", "instances": [{"input": "Night : Day :: Right : Left", "output": "The relation between the given pairs is that they are opposites."}], "is_classification": false},
{"id": "seed_task_2", "name": "one_sentence_description", "instruction": "Generate a one-sentence description for each of the following people.", "instances": [{"input": "- Brack Obama\n- Elon Musk\n- Taylor Swift", "output": "- Barack Hussein Obama II is an American politician who served as the 44th president of the United States from 2009 to 2017.\n- Elon Musk is the founder, CEO, and chief engineer of SpaceX; angel investor, CEO and product architect of Tesla, Inc.; founder of The Boring Company; co-founder of Neuralink and OpenAI; president of the Musk Foundation; and owner and CEO of Twitter, Inc.\n- Taylor Alison Swift is an American singer-songwriter."}], "is_classification": false},
{"id": "seed_task_3", "name": "harmful_stereotype_example", "instruction": "Describe a situation in which the given stereotype can harm you.", "instances": [{"input": "\"All Asians are smart!\"", "output": "Imagine you're a small child of Asian descent. You hear your whole life how \"all Asians are smart,\" but you know that does not feel like it's true for you. You have a hard time in school. Reading is difficult. Math is impossible. Everyone around you seems to pressure you because you are supposed to be \"smart.\" Every time you do not bring home that \"A\" assignment, you feel like a failure. Like you alone missed out on the gene that makes \"all Asians smart.\"\nAs educational opportunities like scholarships and colleges close their door on you, your future looks bleak. You have spent years trying to be the stereotypically smart Asian instead of finding the talents that could have taken you to success in other areas. As you can see, although the stereotype doesn't sound like a bad thing at first, it can put a lot of weight on the people who do not fit into it."}], "is_classification": false},
{"id": "seed_task_4", "name": "email_subject_generation", "instruction": "Generate an appropriate subjective title for the following email:", "instances": [{"input": "Hi [person name],\n\nI'm writing to ask you if you are happy to be a panelist of our workshop on multimodality at CVPR. The workshop will be held on June 20, 2023. \n\nBest,\n[my name]", "output": "Invitition to be a panelist for CVPR 2023 workshop on Multimodality"}], "is_classification": false},
{"id": "seed_task_5", "name": "interview_question_answering", "instruction": "How do you answer this question in a job interview?", "instances": [{"input": "What do you think about when you are alone in a car?", "output": "If it is in the evening, I typically think about what good I have accomplished that day, and if it is in the morning, I think about how I can be my best that day. I also try to practice gratitude and live in the present moment while I'm driving to avoid distractions."}], "is_classification": false},
{"id": "seed_task_6", "name": "new_year's_resolutions", "instruction": "Brainstorm a list of possible New Year's resolutions.", "instances": [{"input": "", "output": "- Lose weight\n- Exercise more\n- Eat healthier\n- Quit smoking\n- Drink less alcohol\n- Spend less money\n- Save more money\n- Get out of debt\n- Take a trip\n- Volunteer more\n- Spend more time with family and friends\n- Read more\n- Start a hobby\n- Get organized\n- Simplify your life\n- Meditate or pray more\n- Get a new job\n- Move to a new city\n- Make new friends\n- Go back to school\n- Learn a new language\n- Start a business"}], "is_classification": false}
.. literalinclude:: ../../../../examples/user/assets/seed_tasks.jsonl
:language: json
:lines: 0-6
:caption: File is available at `tasks.jsonl <https://github.com/IBM/ibm-generative-ai/blob/main/examples/user/assets/seed_tasks.jsonl>`_ on GitHub.
46 changes: 11 additions & 35 deletions documentation/docs/source/rst_source/genai.prompt.quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,40 +26,16 @@ A crucial step is to provide the mapping between the template's variables and th

Assuming local :ref:`synth-animal.yaml<File : synth-animal.yaml>` and :ref:`penguins.csv<File : penguins.csv>` and files.

.. code-block:: python
.. literalinclude:: ../../../../examples/user/prompt_from_all_csv.py
:language: python
:lines: 42-44, 46-66
:caption: Multiple Prompt Patterns using complete file
from genai.prompt_pattern import PromptPattern
_path_to_template_file = "my_templates/synth-animal.yaml"
_path_to_csv_file = "my_data/penguins.csv"
prompt = PromptPattern.from_file(_path_to_template_file)
print("\nGiven template:\n", prompt)
prompt.sub("animal", "penguins")
mapping = {
"species": ["species1", "species2", "species3"],
"island": ["location1", "location2", "location3"],
"flipper_length_mm": ["length1", "length2", "length3"],
"year": ["dob1", "dob2", "dob3"],
}
list_of_prompts = prompt.sub_all_from_csv(
csv_path=_path_to_csv_file,
col_to_var=mapping,
)
print("-----------------------")
print("generated prompt")
print(list_of_prompts)
print(len(list_of_prompts))
print("-----------------------")
responses = bloomz.generate_as_completed(list_of_prompts)
for response in responses:
# do something with response.generated_text
:prepend: from genai.prompt_pattern import PromptPattern
_path_to_template_file = "my_templates/synth-animal.yaml"
_path_to_csv_file = "my_data/penguins.csv"
# Create prompt pattern from the file
prompt = PromptPattern.from_file(_path_to_template_file)
:append: # do something with response.generated_text

.. code-block:: console
:caption: Output
Expand Down Expand Up @@ -134,7 +110,7 @@ Assuming local :ref:`instruction.yaml<File : instruction.yaml>` and :ref:`tasks.
print("-----------------------")
responses = bloomz.generate_as_completed(list_of_prompts)
responses = flan_ul2.generate_as_completed(list_of_prompts)
for response in responses:
# do something with response.generated_text
Expand Down Expand Up @@ -225,7 +201,7 @@ Assuming local :ref:`synth-animal.yaml<File : synth-animal.yaml>` and :ref:`peng
print(len(list_of_prompts))
print("-----------------------")
responses = bloomz.generate_as_completed(list_of_prompts)
responses = flan_ul2.generate_as_completed(list_of_prompts)
for response in responses:
# do something with response.generated_text
Expand Down
86 changes: 86 additions & 0 deletions examples/dev/async-flaky-request-handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import asyncio
import logging
import os
import random

import httpx
from dotenv import load_dotenv

from genai.model import Credentials, Model
from genai.schemas import GenerateParams, ModelType, TokenParams
from genai.services.connection_manager import ConnectionManager
from genai.services.request_handler import RequestHandler

logging.basicConfig(level="ERROR")


class FlakyRequestHandler(RequestHandler):
@staticmethod
async def flaky_async_generate(
endpoint: str, key: str, model_id: str = None, inputs: list = None, parameters: dict = None, options=None
):
"""Low level API for async /generate request to REST API.
Args:
endpoint (str): Remote endpoint to be queried.
key (str): API key for authorization.
model_id (str, optional): The id of the language model to be queried. Defaults to None.
inputs (list, optional): List of inputs to be queried. Defaults to None.
parameters (dict, optional): Key-value pairs for model parameters. Defaults to None.
options (Options, optional): Additional parameters to pass in the query payload. Defaults to None.
Returns:
httpx.Response: Response from the REST API.
"""
headers, json_data = RequestHandler._metadata(
method="POST",
key=key,
model_id=model_id,
inputs=inputs,
parameters=parameters,
options=options,
)

response = None
totalsleep = 0
for attempt in range(0, ConnectionManager.MAX_RETRIES_GENERATE):
response = await ConnectionManager.async_generate_client.post(endpoint, headers=headers, json=json_data)
choice = random.randint(0, 2)
if choice == 0:
response.status_code = httpx.codes.SERVICE_UNAVAILABLE
elif choice == 1:
response.status_code = httpx.codes.TOO_MANY_REQUESTS
else:
pass
if response.status_code in [httpx.codes.SERVICE_UNAVAILABLE, httpx.codes.TOO_MANY_REQUESTS]:
totalsleep += 2 ** (attempt + 1)
print(
"Prompt = {}, Choice = {}, Current sleep {} seconds, Total sleep = {}".format(
json_data["inputs"], choice, 2 ** (attempt + 1), totalsleep
)
)
await asyncio.sleep(2 ** (attempt + 1))
else:
break
return response


RequestHandler.async_generate = FlakyRequestHandler.flaky_async_generate

# make sure you have a .env file under genai root with
# GENAI_KEY=<your-genai-key>
load_dotenv()
api_key = os.getenv("GENAI_KEY", None)
api_url = os.getenv("GENAI_API", None)
creds = Credentials(api_key=api_key, api_endpoint=api_url) # credentials object to access GENAI


# Instantiate parameters for text generation
generate_params = GenerateParams(decoding_method="sample", max_new_tokens=5, min_new_tokens=1)
tokenize_params = TokenParams(return_tokens=True)


flan_ul2 = Model(ModelType.FLAN_UL2, params=generate_params, credentials=creds)
prompts = ["Generate a random number > {}: ".format(i) for i in range(25)]
for response in flan_ul2.generate_async(prompts, ordered=True):
pass
18 changes: 13 additions & 5 deletions examples/dev/async-flaky-responses-ordered.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@


class FlakyAsyncResponseGenerator(AsyncResponseGenerator):
async def _get_response_json(self, model, inputs, params):
async def _get_response_json(self, model, inputs, params, options):
try:
global num_requests
num_requests += 1
if num_requests % 2 == 0:
await asyncio.sleep(random.randint(0, 5))
response_raw = await self.service_fn_(model, inputs, params)
response_raw = await self.service_fn_(model, inputs, params, options)
else:
await asyncio.sleep(random.randint(0, 5))
response_raw = None # bad response
Expand All @@ -35,10 +35,11 @@ def generate_async(
prompts,
ordered: bool = False,
callback=None,
options=None,
):
try:
with FlakyAsyncResponseGenerator(
self.model, prompts, self.params, self.service, ordered=ordered, callback=callback
self.model, prompts, self.params, self.service, ordered=ordered, callback=callback, options=options
) as asynchelper:
for response in asynchelper.generate_response():
yield response
Expand All @@ -47,10 +48,17 @@ def generate_async(
except Exception as ex:
raise GenAiException(ex)

def tokenize_async(self, prompts, ordered=False, callback=None):
def tokenize_async(self, prompts, ordered=False, callback=None, options=None):
try:
with FlakyAsyncResponseGenerator(
self.model, prompts, self.params, self.service, fn="tokenize", ordered=ordered, callback=callback
self.model,
prompts,
self.params,
self.service,
fn="tokenize",
ordered=ordered,
callback=callback,
options=options,
) as asynchelper:
for response in asynchelper.generate_response():
yield response
Expand Down
Loading

0 comments on commit 44fea57

Please sign in to comment.