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