Skip to content

Commit

Permalink
feat(construct): lambda layer (#20)
Browse files Browse the repository at this point in the history
* save current idea for use cases

* add structure for the construct

* updates to test simple working flow

* add initial doc for lambda layer construct

* remove current use case architecture as requires more brainstorming

* remove temp drawio file
  • Loading branch information
krokoko authored Oct 11, 2023
1 parent d45f342 commit fda5e13
Show file tree
Hide file tree
Showing 32 changed files with 1,151 additions and 341 deletions.
2 changes: 2 additions & 0 deletions .gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions .npmignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions .projenrc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,18 +62,20 @@ const project = new awscdk.AwsCdkConstructLibrary({
license: 'Apache-2.0',
copyrightPeriod: '2023-',
copyrightOwner: 'Amazon.com, Inc. or its affiliates. All Rights Reserved.',
gitignore: ['*.DS_STORE', '!.node-version'],
gitignore: ['*.DS_STORE', '!.node-version', '*.pyc', '__pycache__/'],
stability: 'experimental',
sampleCode: false,
stale: true,
});

// Add some useful github workflows
buildMeritBadgerWorkflow(project);
buildMonthlyIssuesMetricsWorkflow(project);
buildUpdateContributorsWorkflow(project);
buildAutoApproveWorkflow(project);

// We don't want to package the use cases
project.npmignore?.addPatterns('/use-cases/');

// Add License header automatically
project.eslint?.addPlugins('header');
project.eslint?.addRules({
Expand Down
4 changes: 4 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ All test files can be found in the /test directory under each construct (or core
|--docs/ (draw.io project containing architecture diagrams for all constructs)
|--lib/ (Build output)
|--lambda/ (Lambda functions code)
|--layers/ (Lambda layers code)
|--resources (If you need additional resources packaged with your library)
|--projenrc (folder containing utilities for the main projenrc file)
|--src/ (Source .ts files)
|--common/ (Common code reused accross constructs)
|--helpers
Expand All @@ -94,6 +96,8 @@ All test files can be found in the /test directory under each construct (or core
|--<emerging-tech-area>
|--<pattern-name>
|--*.test.ts (construct test files)
|--use-cases
|--<use case name>
```


Expand Down
339 changes: 1 addition & 338 deletions docs/emerging_tech_cdk_constructs.drawio

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions layers/langchain-common-deps/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
boto3>=1.28.61
botocore>=1.31.61
requests==2.31.0
requests-aws4auth==1.2.3
langchain==0.0.309
opensearch-py==2.3.1
openai==0.28.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .openai import *
from .sagemaker import *
from .bedrock import *
from .base import Mode
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .base import ModelAdapter, Mode
199 changes: 199 additions & 0 deletions layers/langchain-common-layer/python/genai_core/adapters/base/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import os
from enum import Enum
from langchain import LLMChain
from langchain.callbacks.base import BaseCallbackHandler
from langchain.chains import ConversationalRetrievalChain, ConversationChain
from langchain.memory import ConversationBufferMemory
from langchain.prompts.prompt import PromptTemplate
from langchain.chains.conversational_retrieval.prompts import (
QA_PROMPT,
CONDENSE_QUESTION_PROMPT,
)
from genai_core.utils import PredictionException


class Mode(Enum):
CHAIN = "qa_chain"


class ModelAdapter:
def __init__(self, session_id, user_id, model_kwargs={}, adapter_kwargs={}):
self.session_id = session_id
self.user_id = user_id
self._mode = adapter_kwargs.get("mode", "qa_chain")
self.model_kwargs = model_kwargs

self.callback_handler = BaseCallbackHandler()
self.__bind_callbacks()

history_enabled = adapter_kwargs.get("chat_history", False)
if (history_enabled == True):
self.chat_history = self.get_chat_history()
else:
self.chat_history = None
self.llm = self.get_llm(model_kwargs)

def __bind_callbacks(self):
callback_methods = [method for method in dir(self) if method.startswith("on_")]
valid_callback_names = [
attr for attr in dir(self.callback_handler) if attr.startswith("on_")
]

for method in callback_methods:
if method in valid_callback_names:
setattr(self.callback_handler, method, getattr(self, method))

def get_llm(self, model_kwargs={}):
raise ValueError("llm must be implemented")

def get_embeddings_model(self, embeddings):
raise ValueError("embeddings must be implemented")

def get_chat_history(self):
raise ValueError("get_chat_history must be implemented")

def get_memory(self, output_key=None):
return ConversationBufferMemory(
memory_key="chat_history",
chat_memory=self.chat_history,
return_messages=True,
output_key=output_key,
)

def get_prompt_no_history(self):
template = """\n\nHuman: {context}
Answer from this text: {question}
\n\nAssistant:"""
prompt_template = PromptTemplate(template=template, input_variables=["context", "question"])

return prompt_template

def get_prompt(self):
template = """The following is a friendly conversation between a human and an AI. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
{chat_history}
Question: {input}"""
input_variables = ["input", "chat_history"]
prompt_template_args = {
"chat_history": "{chat_history}",
"input_variables": input_variables,
"template": template,
}
prompt_template = PromptTemplate(**prompt_template_args)

return prompt_template

def get_condense_question_prompt(self):
return CONDENSE_QUESTION_PROMPT

def get_qa_prompt(self):
return QA_PROMPT

def run_with_chain(self, user_prompt, workspace_id=None):
if not self.llm:
raise ValueError("llm must be set")

if workspace_id:
conversation = ConversationalRetrievalChain.from_llm(
self.llm,
condense_question_prompt=self.get_condense_question_prompt(),
combine_docs_chain_kwargs={"prompt": self.get_qa_prompt()},
return_source_documents=True,
memory=self.get_memory(output_key="answer"),
verbose=True,
callbacks=[self.callback_handler],
)
result = conversation({"question": user_prompt})
print(result["source_documents"])
documents = [
{
"page_content": doc.page_content,
"metadata": doc.metadata,
}
for doc in result["source_documents"]
]

metadata = {
"modelId": self.model_id,
"modelKwargs": self.model_kwargs,
"mode": self._mode,
"sessionId": self.session_id,
"userId": self.user_id,
"workspaceId": workspace_id,
"documents": documents,
}

self.chat_history.add_metadata(metadata)

return {
"sessionId": self.session_id,
"type": "text",
"content": result["answer"],
"metadata": metadata,
}

if self.chat_history != None:
conversation = ConversationChain(
llm=self.llm,
prompt=self.get_prompt(),
memory=self.get_memory(),
verbose=True,
)
answer = conversation.predict(
input=user_prompt, callbacks=[self.callback_handler]
)

metadata = {
"modelId": self.model_id,
"modelKwargs": self.model_kwargs,
"mode": self._mode,
"sessionId": self.session_id,
"userId": self.user_id,
"documents": [],
}

self.chat_history.add_metadata(metadata)

return {
"sessionId": self.session_id,
"type": "text",
"content": answer,
"metadata": metadata,
}

chain = LLMChain(llm=self.llm, prompt=self.get_prompt_no_history(), verbose=True)
try:
response = chain.predict(context='', question=user_prompt, callbacks=[self.callback_handler])
raise PredictionException("claude")
except PredictionException as e:
print(e.message)
#TODO return response


metadata = {
"modelId": self.model_id,
"modelKwargs": self.model_kwargs,
"mode": self._mode,
"sessionId": self.session_id,
"userId": self.user_id,
"documents": [],
}

return {
"sessionId": self.session_id,
"type": "text",
"content": response,
"metadata": metadata,
}

def run(self, prompt, workspace_id=None, *args, **kwargs):
print(f"run with {kwargs}")
print(f"workspace_id {workspace_id}")
print(f"mode: {self._mode}")

if self._mode == "qa_chain":
return self.run_with_chain(prompt, workspace_id)

raise ValueError(f"unknown mode {self._mode}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .claude import *
from .titan import *
from .ai21_j2 import *
from .cohere import *
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import genai_core.clients
from langchain.llms import Bedrock
from langchain.prompts.prompt import PromptTemplate

from ..base import ModelAdapter
from ..registry import registry


class AI21J2Adapter(ModelAdapter):
def __init__(self, model_id, *args, **kwargs):
self.model_id = model_id

super().__init__(*args, **kwargs)

def get_llm(self, model_kwargs={}):
bedrock = genai_core.clients.get_bedrock_client()

params = {}
if "temperature" in model_kwargs:
params["temperature"] = model_kwargs["temperature"]
if "topP" in model_kwargs:
params["topP"] = model_kwargs["topP"]
if "maxTokens" in model_kwargs:
params["maxTokens"] = model_kwargs["maxTokens"]

return Bedrock(
client=bedrock,
model_id=self.model_id,
model_kwargs=params,
callbacks=[self.callback_handler],
)

def get_prompt(self):
template = """Human: The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
{chat_history}
Question: {input}
Assistant:"""

input_variables = ["input", "chat_history"]
prompt_template_args = {
"chat_history": "{chat_history}",
"input_variables": input_variables,
"template": template,
}
prompt_template = PromptTemplate(**prompt_template_args)

return prompt_template


# Register the adapter
registry.register(r"^bedrock.ai21.j2*", AI21J2Adapter)
Loading

0 comments on commit fda5e13

Please sign in to comment.