From 914ace691173bb6fa786e2c59b08aa5d36ce0515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Hedenstr=C3=B6m?= <erik@hedenstroem.com> Date: Wed, 6 Sep 2023 12:27:32 +0000 Subject: [PATCH] Confluence --- config.py | 68 +++---- .../confluence_context_provider.py | 56 ++++++ context_providers/gitlab_context_provider.py | 6 +- context_providers/imghdr.py | 175 ------------------ context_providers/jira_context_provider.py | 40 ++-- requirements.txt | 3 +- 6 files changed, 118 insertions(+), 230 deletions(-) create mode 100644 context_providers/confluence_context_provider.py delete mode 100644 context_providers/imghdr.py diff --git a/config.py b/config.py index bfadb58..2b08663 100644 --- a/config.py +++ b/config.py @@ -7,7 +7,11 @@ import os import sys from continuedev.src.continuedev.core.models import Models -from continuedev.src.continuedev.core.config import CustomCommand, SlashCommand, ContinueConfig +from continuedev.src.continuedev.core.config import ( + CustomCommand, + SlashCommand, + ContinueConfig, +) from continuedev.src.continuedev.libs.llm.ollama import Ollama from continuedev.src.continuedev.libs.llm.openai import OpenAI @@ -19,18 +23,24 @@ from continuedev.src.continuedev.plugins.steps.comment_code import CommentCodeSt from continuedev.src.continuedev.plugins.steps.share_session import ShareSessionStep from continuedev.src.continuedev.plugins.steps.main import EditHighlightedCodeStep -from continuedev.src.continuedev.plugins.context_providers.search import SearchContextProvider -from continuedev.src.continuedev.plugins.context_providers.diff import DiffContextProvider +from continuedev.src.continuedev.plugins.context_providers.search import ( + SearchContextProvider, +) +from continuedev.src.continuedev.plugins.context_providers.diff import ( + DiffContextProvider, +) from continuedev.src.continuedev.plugins.context_providers.url import URLContextProvider -from continuedev.src.continuedev.plugins.context_providers.terminal import TerminalContextProvider +from continuedev.src.continuedev.plugins.context_providers.terminal import ( + TerminalContextProvider, +) from dotenv import dotenv_values from continuedev.src.continuedev.libs.util.paths import getGlobalFolderPath from continuedev.src.continuedev.libs.util.logging import logger logger.info(f"sys.version: {sys.version}") -c = dotenv_values(getGlobalFolderPath()+"/.env") -for path in c.get("SYS_PATH","").split(","): +c = dotenv_values(getGlobalFolderPath() + "/.env") +for path in c.get("SYS_PATH", "").split(","): if path != "": abspath = os.path.abspath(path.strip()) if abspath not in sys.path: @@ -39,33 +49,22 @@ for path in c.get("SYS_PATH","").split(","): logger.info(f"sys.path: {', '.join(sys.path)}") from jira_context_provider import JIRAContextProvider +from confluence_context_provider import ConfluenceContextProvider from gitlab_context_provider import GitLabContextProvider config = ContinueConfig( allow_anonymous_telemetry=False, models=Models( - default=Ollama( - context_length=2048, - unique_id="116ea2d7ed102aa7a7c2ab2206dd2083aebcac7a7f853956a9108fd1b1e67e82", - model="codellama", - prompt_templates={ - 'edit': '[INST] Consider the following code:\n```\n{{code_to_edit}}\n```\nEdit the code to perfectly satisfy the following user request:\n{{user_input}}\nOutput nothing except for the code. No code block, no English explanation, no start/end tags.\n[/INST]' - }, - server_url="http://localhost:11434" - ), + default=Ollama(context_length=4096, model="codellama"), unused=[ - OpenAI( - context_length=2048, - model="gpt-4", - prompt_templates={}, - api_key=c.get("OPENAI_API_KEY") - ), - OpenAI( - context_length=2048, - model="gpt-3.5-turbo", - prompt_templates={}, - api_key=c.get("OPENAI_API_KEY")) - ] + Ollama(context_length=4096, model="codellama:7b-code"), + Ollama(context_length=4096, model="codellama:7b-python"), + Ollama(context_length=4096, model="codellama:13b"), + OpenAI(model="gpt-3.5-turbo", api_key=c.get("OPENAI_API_KEY")), + OpenAI(model="gpt-3.5-turbo-16k", api_key=c.get("OPENAI_API_KEY")), + OpenAI(model="gpt-4", api_key=c.get("OPENAI_API_KEY")), + OpenAI(model="gpt-4-32k", api_key=c.get("OPENAI_API_KEY")), + ], ), system_message=None, temperature=0.3, @@ -106,13 +105,13 @@ config = ContinueConfig( name="share", description="Download and share the session transcript", step=ShareSessionStep, - ) + ), ], context_providers=[ SearchContextProvider(), DiffContextProvider(), URLContextProvider( - preset_urls = [ + preset_urls=[ # Add any common urls you reference here so they appear in autocomplete ] ), @@ -120,11 +119,16 @@ config = ContinueConfig( JIRAContextProvider( server=c.get("JIRA_SERVER"), username=c.get("JIRA_USERNAME"), - token=c.get("JIRA_TOKEN") + token=c.get("JIRA_TOKEN"), + ), + ConfluenceContextProvider( + server=c.get("CONFLUENCE_SERVER"), + username=c.get("CONFLUENCE_USERNAME"), + token=c.get("CONFLUENCE_TOKEN"), ), GitLabContextProvider( server=c.get("GITLAB_SERVER"), - token=c.get("GITLAB_TOKEN") - ) + token=c.get("GITLAB_TOKEN"), + ), ], ) diff --git a/context_providers/confluence_context_provider.py b/context_providers/confluence_context_provider.py new file mode 100644 index 0000000..8c9d830 --- /dev/null +++ b/context_providers/confluence_context_provider.py @@ -0,0 +1,56 @@ +from typing import List + +from continuedev.src.continuedev.core.context import ( + ContextProvider, + ContextItem, + ContextItemDescription, + ContextItemId, +) + +from atlassian import Confluence +from markdownify import markdownify as md + + +class ConfluenceContextProvider(ContextProvider): + title = "confluence" + + server: str + username: str + token: str + + CONFLUENCE_CONTEXT_ITEM_ID = "confluence_page" + + @property + def BASE_CONTEXT_ITEM(self): + return ContextItem( + content="", + description=ContextItemDescription( + name="Confluence Page", + description="Enter the id of the Confluence page", + id=ContextItemId( + provider_title=self.title, item_id=self.CONFLUENCE_CONTEXT_ITEM_ID + ), + ), + ) + + async def provide_context_items(self, workspace_dir: str) -> List[ContextItem]: + return [self.BASE_CONTEXT_ITEM] + + async def get_item(self, id: ContextItemId, query: str) -> ContextItem: + if id.item_id != self.CONFLUENCE_CONTEXT_ITEM_ID: + raise Exception("Invalid item id") + confluence = Confluence( + url=self.server, username=self.username, password=self.token, cloud=True + ) + page = confluence.get_page_by_id( + query.lstrip(self.title + " "), + expand="space,body.view", + status=None, + version=None, + ) + ctx_item = self.BASE_CONTEXT_ITEM.copy() + ctx_item.description.name = f"Page {page['id']}" + ctx_item.description.description = f"{page['space']['name']} / {page['title']}" + ctx_item.description.id.item_id = page["id"] + ctx_item.content = md(page["body"]["view"]["value"]) + return ctx_item diff --git a/context_providers/gitlab_context_provider.py b/context_providers/gitlab_context_provider.py index c9a7161..a17e5a0 100644 --- a/context_providers/gitlab_context_provider.py +++ b/context_providers/gitlab_context_provider.py @@ -9,18 +9,16 @@ from continuedev.src.continuedev.core.context import ( from gitlab import Gitlab -class GitLabContextProvider(ContextProvider): +class GitLabContextProvider(ContextProvider): title = "gitlab" server: str token: str async def provide_context_items(self, workspace_dir: str) -> List[ContextItem]: - gl = Gitlab(url=self.server, private_token=self.token) - issues = gl.issues.list(state='opened',scope='all',per_page=100) - + issues = gl.issues.list(state="opened", scope="all", per_page=100) return [ ContextItem( content=issue.description, diff --git a/context_providers/imghdr.py b/context_providers/imghdr.py deleted file mode 100644 index 6a372e6..0000000 --- a/context_providers/imghdr.py +++ /dev/null @@ -1,175 +0,0 @@ -"""Recognize image file formats based on their first few bytes.""" - -from os import PathLike -import warnings - -__all__ = ["what"] - - -warnings._deprecated(__name__, remove=(3, 13)) - - -#-------------------------# -# Recognize image headers # -#-------------------------# - -def what(file, h=None): - f = None - try: - if h is None: - if isinstance(file, (str, PathLike)): - f = open(file, 'rb') - h = f.read(32) - else: - location = file.tell() - h = file.read(32) - file.seek(location) - for tf in tests: - res = tf(h, f) - if res: - return res - finally: - if f: f.close() - return None - - -#---------------------------------# -# Subroutines per image file type # -#---------------------------------# - -tests = [] - -def test_jpeg(h, f): - """JPEG data with JFIF or Exif markers; and raw JPEG""" - if h[6:10] in (b'JFIF', b'Exif'): - return 'jpeg' - elif h[:4] == b'\xff\xd8\xff\xdb': - return 'jpeg' - -tests.append(test_jpeg) - -def test_png(h, f): - if h.startswith(b'\211PNG\r\n\032\n'): - return 'png' - -tests.append(test_png) - -def test_gif(h, f): - """GIF ('87 and '89 variants)""" - if h[:6] in (b'GIF87a', b'GIF89a'): - return 'gif' - -tests.append(test_gif) - -def test_tiff(h, f): - """TIFF (can be in Motorola or Intel byte order)""" - if h[:2] in (b'MM', b'II'): - return 'tiff' - -tests.append(test_tiff) - -def test_rgb(h, f): - """SGI image library""" - if h.startswith(b'\001\332'): - return 'rgb' - -tests.append(test_rgb) - -def test_pbm(h, f): - """PBM (portable bitmap)""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': - return 'pbm' - -tests.append(test_pbm) - -def test_pgm(h, f): - """PGM (portable graymap)""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': - return 'pgm' - -tests.append(test_pgm) - -def test_ppm(h, f): - """PPM (portable pixmap)""" - if len(h) >= 3 and \ - h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': - return 'ppm' - -tests.append(test_ppm) - -def test_rast(h, f): - """Sun raster file""" - if h.startswith(b'\x59\xA6\x6A\x95'): - return 'rast' - -tests.append(test_rast) - -def test_xbm(h, f): - """X bitmap (X10 or X11)""" - if h.startswith(b'#define '): - return 'xbm' - -tests.append(test_xbm) - -def test_bmp(h, f): - if h.startswith(b'BM'): - return 'bmp' - -tests.append(test_bmp) - -def test_webp(h, f): - if h.startswith(b'RIFF') and h[8:12] == b'WEBP': - return 'webp' - -tests.append(test_webp) - -def test_exr(h, f): - if h.startswith(b'\x76\x2f\x31\x01'): - return 'exr' - -tests.append(test_exr) - -#--------------------# -# Small test program # -#--------------------# - -def test(): - import sys - recursive = 0 - if sys.argv[1:] and sys.argv[1] == '-r': - del sys.argv[1:2] - recursive = 1 - try: - if sys.argv[1:]: - testall(sys.argv[1:], recursive, 1) - else: - testall(['.'], recursive, 1) - except KeyboardInterrupt: - sys.stderr.write('\n[Interrupted]\n') - sys.exit(1) - -def testall(list, recursive, toplevel): - import sys - import os - for filename in list: - if os.path.isdir(filename): - print(filename + '/:', end=' ') - if recursive or toplevel: - print('recursing down:') - import glob - names = glob.glob(os.path.join(glob.escape(filename), '*')) - testall(names, recursive, 0) - else: - print('*** directory (use -r) ***') - else: - print(filename + ':', end=' ') - sys.stdout.flush() - try: - print(what(filename)) - except OSError: - print('*** not found ***') - -if __name__ == '__main__': - test() diff --git a/context_providers/jira_context_provider.py b/context_providers/jira_context_provider.py index ffea686..ca8e38c 100644 --- a/context_providers/jira_context_provider.py +++ b/context_providers/jira_context_provider.py @@ -1,28 +1,34 @@ from typing import List -from continuedev.src.continuedev.libs.util.logging import logger -from continuedev.src.continuedev.core.context import ContextProvider, ContextItem, ContextItemDescription, ContextItemId +from continuedev.src.continuedev.core.context import ( + ContextProvider, + ContextItem, + ContextItemDescription, + ContextItemId, +) -from jira import JIRA +from atlassian import Jira -class JIRAContextProvider(ContextProvider): +class JIRAContextProvider(ContextProvider): title = "jira" - name = "JIRA" - description = "Reference the contents of a JIRA issue" server: str username: str token: str + JIRA_CONTEXT_ITEM_ID = "jira_issue" + @property def BASE_CONTEXT_ITEM(self): return ContextItem( content="", description=ContextItemDescription( - name=self.name, - description=self.description, - id=ContextItemId(provider_title=self.title, item_id=self.title), + name="Jira Issue", + description="Enter the key of the Jira issue", + id=ContextItemId( + provider_title=self.title, item_id=self.JIRA_CONTEXT_ITEM_ID + ), ), ) @@ -30,17 +36,15 @@ class JIRAContextProvider(ContextProvider): return [self.BASE_CONTEXT_ITEM] async def get_item(self, id: ContextItemId, query: str) -> ContextItem: - if id.item_id != self.title: + if id.item_id != self.JIRA_CONTEXT_ITEM_ID: raise Exception("Invalid item id") - jira = JIRA( - server=self.server, - basic_auth=(self.username, self.token), - logging=True + jira = Jira( + url=self.server, username=self.username, password=self.token, cloud=True ) issue = jira.issue(query.lstrip(self.title + " ")) ctx_item = self.BASE_CONTEXT_ITEM.copy() - ctx_item.description.name = issue.key - ctx_item.description.description = issue.fields.summary - ctx_item.description.id.item_id = issue.id - ctx_item.content = issue.fields.description + ctx_item.description.name = issue["key"] + ctx_item.description.description = issue["fields"]["summary"] + ctx_item.description.id.item_id = issue["id"] + ctx_item.content = issue["fields"]["description"] return ctx_item diff --git a/requirements.txt b/requirements.txt index fca13ec..398a160 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ -jira==3.5.2 +atlassian-python-api==3.41.1 python-gitlab==3.15.0 +markdownify==0.11.6 -- GitLab