Skip to content

Commit

Permalink
Feature/devai api (#7)
Browse files Browse the repository at this point in the history
* Added devai-api - Cloud Run + FastAPI app to expose /generate endpoint for JIRA integration
* Added sample JIRA Forge app for custom button
  • Loading branch information
gitrey authored Apr 18, 2024
1 parent ed3c8df commit f0364ea
Show file tree
Hide file tree
Showing 16 changed files with 11,316 additions and 0 deletions.
32 changes: 32 additions & 0 deletions devai-api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.11-slim

# Allow statements and log messages to immediately appear in the logs
ENV PYTHONUNBUFFERED True

WORKDIR /app

# Install production dependencies.
COPY ./requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

# Copy local code to the container image.
COPY . ./

# Run the web service on container startup.
CMD ["python", "run_app.py"]
15 changes: 15 additions & 0 deletions devai-api/app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from .app import EMBEDDING_MODEL_NAME, init_app, parse_config
56 changes: 56 additions & 0 deletions devai-api/app/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from contextlib import asynccontextmanager
from ipaddress import IPv4Address, IPv6Address
from typing import Optional

import yaml
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from langchain.embeddings import VertexAIEmbeddings
from pydantic import BaseModel


from .routes import routes

EMBEDDING_MODEL_NAME = "textembedding-gecko@001"


class AppConfig(BaseModel):
host: IPv4Address | IPv6Address = IPv4Address("127.0.0.1")
port: int = 8080


def parse_config(path: str) -> AppConfig:
with open(path, "r") as file:
config = yaml.safe_load(file)
return AppConfig(**config)


def init_app(cfg: AppConfig) -> FastAPI:
app = FastAPI()

app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

app.include_router(routes)

return app
103 changes: 103 additions & 0 deletions devai-api/app/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
from typing import Any, Mapping, Optional

from fastapi import APIRouter, Depends, HTTPException, Request
from fastapi.responses import PlainTextResponse, RedirectResponse
from fastapi import APIRouter, Body, FastAPI, HTTPException, Request


from google.auth.transport import requests # type:ignore
from google.oauth2 import id_token # type:ignore
from langchain.embeddings.base import Embeddings

from langchain.agents import AgentType, initialize_agent
from langchain_community.agent_toolkits.gitlab.toolkit import GitLabToolkit
from langchain_community.utilities.gitlab import GitLabAPIWrapper
from langchain_google_vertexai import ChatVertexAI

from vertexai.generative_models import GenerativeModel

model_name="gemini-1.5-pro-preview-0409"

llm = ChatVertexAI(model_name=model_name,
convert_system_message_to_human=True,
temperature=0.2,
max_output_tokens=4096)

gitlab = GitLabAPIWrapper()
toolkit = GitLabToolkit.from_gitlab_api_wrapper(gitlab)

agent = initialize_agent(
toolkit.get_tools(),
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
handle_parsing_errors=True,
max_iterations=5,
return_intermediate_steps=True,
early_stopping_method="generate",
)

routes = APIRouter()
code_chat_model = GenerativeModel(model_name)

@routes.get("/")
async def root():
"""Index endpoint"""
return {"message": "DevAI API"}

@routes.get("/test")
async def test():
"""Test endpoint"""
code_chat = code_chat_model.start_chat()
response = code_chat.send_message("Tell me about Google Gemini 1.5 capabilities")
print(f"Response from Model:\n{response.text}\n")
return {"message": response.text}

@routes.post("/generate", response_class=PlainTextResponse)
async def generate_handler(request: Request, prompt: str = Body(embed=True)):
"""Handler for Generate Content Requests"""
# Retrieve user prompt
if not prompt:
raise HTTPException(status_code=400, detail="Error: Prompt is required")

instructions = f"""You are principal software engineer at Google and given requirements below for implementation.
Please provide implementation details and document the implementation.
REQUIREMENTS:
{prompt}
"""

code_chat = code_chat_model.start_chat()
response = code_chat.send_message(instructions)
print(f"Response from Model:\n{response.text}\n")
# GitLab Agent Call
# resp_text = response.candidates[0].content.parts[0].text

# pr_prompt = f"""Create GitLab merge request using provided details below.
# Create new files, commit them and push them to opened merge request.
# When creating new files, remove the lines that start with ``` before saving the files.

# DETAILS:
# {resp_text}
# """

# print(pr_prompt)
# agent.invoke(pr_prompt)

return response.text

2 changes: 2 additions & 0 deletions devai-api/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
host: 0.0.0.0
port: 8080
19 changes: 19 additions & 0 deletions devai-api/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
fastapi==0.109.2
google-cloud-aiplatform==1.47.0
google-auth==2.27.0
itsdangerous==2.1.2
jinja2==3.1.3
langchain==0.1.8
langchain_google_vertexai==0.0.5
markdown==3.5.2
types-Markdown==3.5.0.20240129
uvicorn[standard]==0.27.0.post1
python-multipart==0.0.7
langsmith==0.1.5
langchain_core==0.1.25
google-generativeai==0.4.0
python-gitlab==4.4.0
PyGithub==2.2.0
jira==3.6.0
pydantic==2.6.1
atlassian-python-api==3.41.10
36 changes: 36 additions & 0 deletions devai-api/run_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2024 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


import asyncio

import uvicorn

from app import init_app, parse_config


async def main():
cfg = parse_config("./config.yml")
app = init_app(cfg)

if app is None:
raise TypeError("app not instantiated")
server = uvicorn.Server(
uvicorn.Config(app, host=str(cfg.host), port=cfg.port, log_level="info")
)
await server.serve()


if __name__ == "__main__":
asyncio.run(main())
15 changes: 15 additions & 0 deletions sample-devai-jira-ui/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"parserOptions": {
"sourceType": "module",
"ecmaVersion": 2017,
"ecmaFeatures": {
"jsx": true
}
},
"plugins": [
"react-hooks"
],
"rules": {
"react-hooks/rules-of-hooks": "error"
}
}
50 changes: 50 additions & 0 deletions sample-devai-jira-ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# These are some examples of commonly ignored file patterns.
# You should customize this list as applicable to your project.
# Learn more about .gitignore:
# https://www.atlassian.com/git/tutorials/saving-changes/gitignore

# Node artifact files
node_modules/
dist/

# Compiled Java class files
*.class

# Compiled Python bytecode
*.py[cod]

# Log files
*.log

# Package files
*.jar

# Maven
target/
dist/

# JetBrains IDE
.idea/

# Unit test reports
TEST*.xml

# Generated by MacOS
.DS_Store

# Generated by Windows
Thumbs.db

# Applications
*.app
*.exe
*.war

# Large media files
*.mp4
*.tiff
*.avi
*.flv
*.mov
*.wmv

39 changes: 39 additions & 0 deletions sample-devai-jira-ui/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Forge Hello World

This project contains a Forge app written in Javascript that displays `Hello World!` in a Jira issue panel.

See [developer.atlassian.com/platform/forge/](https://developer.atlassian.com/platform/forge) for documentation and tutorials explaining Forge.

## Requirements

See [Set up Forge](https://developer.atlassian.com/platform/forge/set-up-forge/) for instructions to get set up.

## Quick start

- Modify your app frontend by editing the `src/frontend/index.jsx` file.

- Modify your app backend by editing the `src/resolvers/index.js` file to define resolver functions. See [Forge resolvers](https://developer.atlassian.com/platform/forge/runtime-reference/custom-ui-resolver/) for documentation on resolver functions.

- Build and deploy your app by running:
```
forge deploy
```

- Install your app in an Atlassian site by running:
```
forge install
```

- Develop your app by running `forge tunnel` to proxy invocations locally:
```
forge tunnel
```

### Notes
- Use the `forge deploy` command when you want to persist code changes.
- Use the `forge install` command when you want to install the app on a new site.
- Once the app is installed on a site, the site picks up the new app changes you deploy without needing to rerun the install command.

## Support

See [Get help](https://developer.atlassian.com/platform/forge/get-help/) for how to get help and provide feedback.
Loading

0 comments on commit f0364ea

Please sign in to comment.