Skip to content

Commit

Permalink
Merge pull request #62 from pieces-app/update-ci
Browse files Browse the repository at this point in the history
update CI versioning
  • Loading branch information
bishoy-at-pieces authored Sep 8, 2024
2 parents 8dbb696 + c8fabe3 commit 9fc1c6c
Show file tree
Hide file tree
Showing 14 changed files with 331 additions and 83 deletions.
13 changes: 12 additions & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
branches:
- main
tags:
- 'v*.*.*'
- '*.*.*'

jobs:
build:
Expand All @@ -22,6 +22,11 @@ jobs:
with:
python-version: 3.9

- name: Get version
id: get_version
run: echo "VERSION=$(echo $GITHUB_REF | cut -d / -f 3)" >> $GITHUB_ENV


- name: Install dependencies
run: |
pip install poetry -U
Expand All @@ -33,6 +38,12 @@ jobs:
- name: Run the tests
run: poetry run pytest

- name: Update version
run: |
echo "Version: ${{ steps.get_version.outputs.VERSION }}"
poetry version ${{ steps.get_version.outputs.VERSION }}
sed -i "s/__version__ = .*/__version__ = \"${{ steps.get_version.outputs.VERSION }}\"/" __init__.py
- name: Build and publish
if: startsWith(github.ref, 'refs/tags/v')
env:
Expand Down
64 changes: 54 additions & 10 deletions src/pieces_os_client/wrapper/basic_identifier/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
Shares
)

from typing import Optional
from typing import Literal, Optional, List
from .basic import Basic
from .user import BasicUser

Expand Down Expand Up @@ -63,14 +63,19 @@ def raw_content(self) -> Optional[str]:
raise ValueError('Unable to get OCR content')
return content
else:
return (
self.asset.original.reference.fragment.string.raw or
self.asset.preview.base.reference.fragment.string.raw or
''
)
try:
fragment_raw = self.asset.original.reference.fragment.string.raw
except AttributeError:
fragment_raw = ""
try:
preview_raw = self.asset.preview.base.reference.fragment.string.raw
except AttributeError:
preview_raw = ""

return preview_raw or fragment_raw or ''

@raw_content.setter
def raw_content(self, content: str):
def raw_content(self, content: str) -> str:
"""
Edit the original format of the asset.
Expand Down Expand Up @@ -155,7 +160,7 @@ def name(self) -> str:
Get the name of the asset.
Returns:
Optional[str]: The name of the asset if available, otherwise "Unnamed snippet".
str: The name of the asset if available, otherwise "Unnamed snippet".
"""
return self.asset.name if self.asset.name else "Unnamed snippet"

Expand All @@ -170,7 +175,7 @@ def name(self, name: str):
self._edit_asset(self.asset)

@property
def description(self):
def description(self) -> Optional[str]:
"""
Retrieve the description of the asset.
Expand Down Expand Up @@ -199,7 +204,7 @@ def annotations(self) -> Optional[Annotations]:
return getattr(self.asset.annotations,"iterable",None)


def delete(self):
def delete(self) -> None:
"""
Delete the asset.
"""
Expand Down Expand Up @@ -246,6 +251,44 @@ def share_raw_content(cls,raw_content:str) -> Shares:
"""
return cls._share(seed = cls._get_seed(raw_content))


@staticmethod
def search(query:str,search_type:Literal["fts","ncs","fuzzy"] = "fts") -> Optional[List["BasicAsset"]]:
"""
Perform a search using either Full Text Search (FTS) or Neural Code Search (NCS) or Fuzzy search (fuzzy).
Parameters:
query (str): The search query string.
search_type (Literal["fts", "ncs", "fuzzy"], optional): The type of search to perform.
'fts' for Full Text Search (default) or 'ncs' for Neural Code Search.
Returns:
Optional[List["BasicAsset"]]: A list of search results or None if no results are found.
"""
if search_type == 'ncs':
results = AssetSnapshot.pieces_client.search_api.neural_code_search(query=query)
elif search_type == 'fts':
results = AssetSnapshot.pieces_client.search_api.full_text_search(query=query)
elif search_type == "fuzzy":
results = AssetSnapshot.pieces_client.assets_api.search_assets(query=query,transferables=False)

if results:
# Extract the iterable which contains the search results
iterable_list = results.iterable if hasattr(results, 'iterable') else []

# Check if iterable_list is a list and contains SearchedAsset objects
if isinstance(iterable_list, list) and all(hasattr(asset, 'exact') and hasattr(asset, 'identifier') for asset in iterable_list):
# Extracting suggested and exact IDs
suggested_ids = [asset.identifier for asset in iterable_list if not asset.exact]
exact_ids = [asset.identifier for asset in iterable_list if asset.exact]

# Combine and store best and suggested matches in asset_ids
combined_ids = exact_ids + suggested_ids

# Print the combined asset details
if combined_ids:
return [BasicAsset(id) for id in combined_ids]

@staticmethod
def _get_seed(raw: str, metadata: Optional[FragmentMetadata] = None) -> Seed:
return Seed(
Expand Down Expand Up @@ -335,3 +378,4 @@ def _share(asset=None,seed=None):
**kwargs
)
)

7 changes: 4 additions & 3 deletions src/pieces_os_client/wrapper/basic_identifier/chat.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pieces_os_client.models.conversation import Conversation
from pieces_os_client import Conversation,Annotations
from ..streamed_identifiers import ConversationsSnapshot
from typing import Optional, List
from .basic import Basic
Expand All @@ -16,7 +16,7 @@ def conversation(self) -> Conversation:
return conversation

@property
def id(self):
def id(self) -> str:
"""
Gets the ID of the conversation.
Expand Down Expand Up @@ -62,7 +62,7 @@ def messages(self) -> List[BasicMessage]:


@property
def annotations(self):
def annotations(self) -> Optional[Annotations]:
"""
Gets the annotations of the conversation.
Expand All @@ -87,3 +87,4 @@ def _edit_conversation(conversation):
"""
ConversationsSnapshot.pieces_client.conversation_api.conversation_update(False, conversation)


122 changes: 79 additions & 43 deletions src/pieces_os_client/wrapper/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,29 +20,72 @@
WellKnownApi,
OSApi,
AllocationsApi,
SearchApi,
__version__
)
from typing import Optional,Dict
import platform
import atexit
import subprocess

import urllib.request
import urllib.error

from .copilot import Copilot
from .basic_identifier import BasicAsset,BasicUser
from .streamed_identifiers import AssetSnapshot
from .websockets import *


class PiecesClient:
def __init__(self, host:str="", seeded_connector: Optional[SeededConnectorConnection] = None,**kwargs):
if host:
self.host = host
else:
self.host = "http://localhost:5323" if 'Linux' in platform.platform() else "http://localhost:1000"
if not host:
host = "http://localhost:5323" if 'Linux' in platform.platform() else "http://localhost:1000"

self.host = host
self._is_started_runned = False
self.local_os = platform.system().upper() if platform.system().upper() in ["WINDOWS","LINUX","DARWIN"] else "WEB"
self.local_os = "MACOS" if self.local_os == "DARWIN" else self.local_os
self._seeded_connector = seeded_connector or SeededConnectorConnection(
application=SeededTrackedApplication(
name = "OPEN_SOURCE",
platform = self.local_os,
version = __version__))
self._connect_websockets = kwargs.get("connect_wesockets",True)
self.user = BasicUser(self)
self.copilot = Copilot(self)
self._startup()


def _startup(self) -> bool:
if self._is_started_runned: return True
if not self.is_pieces_running(): return False

self.api_client = ApiClient(Configuration(self.host))
self._is_started_runned = True
self.tracked_application = self.connector_api.connect(seeded_connector_connection=self._seeded_connector).application

if self._connect_websockets:
self.conversation_ws = ConversationWS(self)
self.assets_ws = AssetsIdentifiersWS(self)
self.user_websocket = AuthWS(self,self.user.on_user_callback)
# Start all initilized websockets
BaseWebsocket.start_all()

self.models = None
self.model_name = "GPT-3.5-turbo Chat Model"
return True


@property
def host(self) -> str:
return self._host

@host.setter
def host(self,host:str):
if not host.startswith("http"):
raise TypeError("Invalid host url\n Host should start with http or https")

self._host = host
self.api_client = ApiClient(Configuration(host))
self.conversation_message_api = ConversationMessageApi(self.api_client)
self.conversation_messages_api = ConversationMessagesApi(self.api_client)
self.conversations_api = ConversationsApi(self.api_client)
Expand All @@ -59,42 +102,16 @@ def __init__(self, host:str="", seeded_connector: Optional[SeededConnectorConnec
self.os_api = OSApi(self.api_client)
self.allocations_api = AllocationsApi(self.api_client)
self.linkfy_api = LinkifyApi(self.api_client)
self.search_api = SearchApi(self.api_client)

# Websocket urls
if not self.host.startswith("http"):
raise ValueError("Invalid host url\n Host should start with http or https")
ws_base_url:str = self.host.replace('http','ws')

ws_base_url:str = host.replace('http','ws')
self.ASSETS_IDENTIFIERS_WS_URL = ws_base_url + "/assets/stream/identifiers"
self.AUTH_WS_URL = ws_base_url + "/user/stream"
self.ASK_STREAM_WS_URL = ws_base_url + "/qgpt/stream"
self.CONVERSATION_WS_URL = ws_base_url + "/conversations/stream/identifiers"
self.HEALTH_WS_URL = ws_base_url + "/.well-known/stream/health"

self.local_os = platform.system().upper() if platform.system().upper() in ["WINDOWS","LINUX","DARWIN"] else "WEB"
self.local_os = "MACOS" if self.local_os == "DARWIN" else self.local_os
seeded_connector = seeded_connector or SeededConnectorConnection(
application=SeededTrackedApplication(
name = "OPEN_SOURCE",
platform = self.local_os,
version = __version__))

self.tracked_application = self.connector_api.connect(seeded_connector_connection=seeded_connector).application

self.user = BasicUser(self)

if kwargs.get("connect_wesockets",True):
self.conversation_ws = ConversationWS(self)
self.assets_ws = AssetsIdentifiersWS(self)
self.user_websocket = AuthWS(self,self.user.on_user_callback)
# Start all initilized websockets
BaseWebsocket.start_all()

self.models = None
self.model_name = "GPT-3.5-turbo Chat Model"
self.copilot = Copilot(self)


def assets(self):
self.ensure_initialization()
return [BasicAsset(id) for id in AssetSnapshot.identifiers_snapshot.keys()]
Expand Down Expand Up @@ -136,6 +153,7 @@ def ensure_initialization(self):
"""
Waits for all the assets/conversations and all the started websockets to open
"""
self._check_startup()
BaseWebsocket.wait_all()

def close(self):
Expand All @@ -150,31 +168,49 @@ def version(self) -> str:
Returns Pieces OS Version
"""
return self.well_known_api.get_well_known_version()

@property
def health(self) -> bool:
def health(self) -> str:
"""
Returns True Pieces OS health is ok else False
Calls the well known health api
/.well-known/health [GET]
"""
try:
return self.well_known_api.get_well_known_health_with_http_info().status_code == 200
except:
pass
return False
return self.well_known_api.get_well_known_health()


def open_pieces_os(self) -> bool:
"""
Open Pieces OS
Returns (bool): true if Pieces OS runned successfully else false
"""
if self.is_pieces_running(): return True
if self.local_os == "WINDOWS":
subprocess.run(["start", "pieces://launch"], shell=True)
elif self.local_os == "MACOS":
subprocess.run(["open","pieces://launch"])
elif self.local_os == "LINUX":
subprocess.run(["xdg-open","pieces://launch"])
return self.health
return self.is_pieces_running(maxium_retries=3)


def is_pieces_running(self,maxium_retries=1) -> bool:
"""
Checks if Pieces OS is running or not
Returns (bool): true if Pieces OS is running
"""
for _ in range(maxium_retries):
try:
with urllib.request.urlopen(f"{self.host}/.well-known/health", timeout=1) as response:
return response.status == 200
except:
pass
return False

def _check_startup(self):
if not self._startup():
raise ValueError("PiecesClient is not started successfully\nPerhaps Pieces OS is not running")

# Register the function to be called on exit
atexit.register(BaseWebsocket.close_all)
Expand Down
12 changes: 6 additions & 6 deletions src/pieces_os_client/wrapper/context.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, List

from .basic_identifier import BasicAsset,BasicMessage
import os
Expand All @@ -10,10 +10,10 @@
class Context:
def __init__(self,
pieces_client:"PiecesClient",
paths:list[str] = [],
raw_assets:list[str] = [],
assets:list[BasicAsset] = [],
messages:list[BasicMessage] = []):
paths:List[str] = [],
raw_assets:List[str] = [],
assets:List[BasicAsset] = [],
messages:List[BasicMessage] = []):
self.pieces_client = pieces_client
self.raw_assets = raw_assets
self.paths = paths
Expand Down Expand Up @@ -67,7 +67,7 @@ def _check_paths(paths):
raise ValueError("Invalid path in the context")

@staticmethod
def _check_raw_assets(assets:list[str]):
def _check_raw_assets(assets:List[str]):
seed_list = Seeds(iterable=[])
for raw in assets:
if not isinstance(raw,str):
Expand Down
Loading

0 comments on commit 9fc1c6c

Please sign in to comment.