summaryrefslogtreecommitdiff
path: root/continuedev/src
diff options
context:
space:
mode:
Diffstat (limited to 'continuedev/src')
-rw-r--r--continuedev/src/continuedev/core/autopilot.py8
-rw-r--r--continuedev/src/continuedev/core/config.py3
-rw-r--r--continuedev/src/continuedev/core/context.py9
-rw-r--r--continuedev/src/continuedev/core/sdk.py2
-rw-r--r--continuedev/src/continuedev/libs/llm/__init__.py14
-rw-r--r--continuedev/src/continuedev/libs/util/devdata.py67
-rw-r--r--continuedev/src/continuedev/libs/util/paths.py15
-rw-r--r--continuedev/src/continuedev/plugins/steps/chat.py8
-rw-r--r--continuedev/src/continuedev/plugins/steps/core/core.py7
-rw-r--r--continuedev/src/continuedev/server/ide.py3
10 files changed, 135 insertions, 1 deletions
diff --git a/continuedev/src/continuedev/core/autopilot.py b/continuedev/src/continuedev/core/autopilot.py
index 8ac7241d..cbf9ad59 100644
--- a/continuedev/src/continuedev/core/autopilot.py
+++ b/continuedev/src/continuedev/core/autopilot.py
@@ -11,6 +11,7 @@ from openai import error as openai_errors
from pydantic import root_validator
from ..libs.util.create_async_task import create_async_task
+from ..libs.util.devdata import dev_data_logger
from ..libs.util.edit_config import edit_config_property
from ..libs.util.logging import logger
from ..libs.util.paths import getSavedContextGroupsPath
@@ -356,6 +357,9 @@ class Autopilot(ContinueBaseModel):
posthog_logger.capture_event(
"step run", {"step_name": step.name, "params": step.dict()}
)
+ dev_data_logger.capture(
+ "step_run", {"step_name": step.name, "params": step.dict()}
+ )
if not is_future_step:
# Check manual edits buffer, clear out if needed by creating a ManualEditStep
@@ -532,6 +536,7 @@ class Autopilot(ContinueBaseModel):
date_created=str(time.time()),
workspace_directory=self.ide.workspace_directory,
)
+ dev_data_logger.capture("new_session", self.session_info.dict())
create_async_task(
create_title(),
@@ -602,6 +607,9 @@ class Autopilot(ContinueBaseModel):
posthog_logger.capture_event(
"select_context_group", {"title": id, "length": len(context_group)}
)
+ dev_data_logger.capture(
+ "select_context_group", {"title": id, "items": context_group}
+ )
async def delete_context_group(self, id: str):
if id not in self._saved_context_groups:
diff --git a/continuedev/src/continuedev/core/config.py b/continuedev/src/continuedev/core/config.py
index 68b2b17d..b513e22a 100644
--- a/continuedev/src/continuedev/core/config.py
+++ b/continuedev/src/continuedev/core/config.py
@@ -53,8 +53,9 @@ class ContinueConfig(BaseModel):
on_traceback: Optional[Step] = None
system_message: Optional[str] = None
policy_override: Optional[Policy] = None
-
context_providers: List[ContextProvider] = []
+ user_token: Optional[str] = None
+ data_server_url: Optional[str] = "https://us-west1-autodebug.cloudfunctions.net"
@validator("temperature", pre=True)
def temperature_validator(cls, v):
diff --git a/continuedev/src/continuedev/core/context.py b/continuedev/src/continuedev/core/context.py
index bb2c43dc..47763d8b 100644
--- a/continuedev/src/continuedev/core/context.py
+++ b/continuedev/src/continuedev/core/context.py
@@ -7,6 +7,7 @@ from meilisearch_python_async import Client
from pydantic import BaseModel
from ..libs.util.create_async_task import create_async_task
+from ..libs.util.devdata import dev_data_logger
from ..libs.util.logging import logger
from ..libs.util.telemetry import posthog_logger
from ..server.meilisearch_server import poll_meilisearch_running
@@ -327,6 +328,14 @@ class ContextManager:
"query": query,
},
)
+ dev_data_logger.capture(
+ "select_context_item",
+ {
+ "provider_title": id.provider_title,
+ "item_id": id.item_id,
+ "query": query,
+ },
+ )
await self.context_providers[id.provider_title].add_context_item(id, query)
async def delete_context_with_ids(self, ids: List[str]):
diff --git a/continuedev/src/continuedev/core/sdk.py b/continuedev/src/continuedev/core/sdk.py
index b25d4566..ddcf6e55 100644
--- a/continuedev/src/continuedev/core/sdk.py
+++ b/continuedev/src/continuedev/core/sdk.py
@@ -3,6 +3,7 @@ import traceback
from typing import Coroutine, List, Optional, Union
from ..libs.llm import LLM
+from ..libs.util.devdata import dev_data_logger
from ..libs.util.logging import logger
from ..libs.util.paths import getConfigFilePath
from ..libs.util.telemetry import posthog_logger
@@ -109,6 +110,7 @@ class ContinueSDK(AbstractContinueSDK):
# When the config is loaded, setup posthog logger
posthog_logger.setup(sdk.ide.unique_id, sdk.config.allow_anonymous_telemetry)
+ dev_data_logger.setup(sdk.config.user_token, sdk.config.data_server_url)
return sdk
diff --git a/continuedev/src/continuedev/libs/llm/__init__.py b/continuedev/src/continuedev/libs/llm/__init__.py
index dcb0d2ef..85ca1969 100644
--- a/continuedev/src/continuedev/libs/llm/__init__.py
+++ b/continuedev/src/continuedev/libs/llm/__init__.py
@@ -3,6 +3,7 @@ from typing import Any, Callable, Coroutine, Dict, Generator, List, Optional, Un
from pydantic import validator
from ...core.main import ChatMessage
+from ...libs.util.devdata import dev_data_logger
from ...models.main import ContinueBaseModel
from ..util.count_tokens import (
DEFAULT_ARGS,
@@ -172,6 +173,10 @@ class LLM(ContinueBaseModel):
completion += chunk
self.write_log(f"Completion: \n\n{completion}")
+ dev_data_logger.capture(
+ "tokens_generated",
+ {"model": self.model, "tokens": self.count_tokens(completion)},
+ )
async def complete(
self,
@@ -209,6 +214,11 @@ class LLM(ContinueBaseModel):
completion = await self._complete(prompt=prompt, options=options)
self.write_log(f"Completion: \n\n{completion}")
+ dev_data_logger.capture(
+ "tokens_generated",
+ {"model": self.model, "tokens": self.count_tokens(completion)},
+ )
+
return completion
async def stream_chat(
@@ -261,6 +271,10 @@ class LLM(ContinueBaseModel):
completion += chunk
self.write_log(f"Completion: \n\n{completion}")
+ dev_data_logger.capture(
+ "tokens_generated",
+ {"model": self.model, "tokens": self.count_tokens(completion)},
+ )
def _stream_complete(
self, prompt, options: CompletionOptions
diff --git a/continuedev/src/continuedev/libs/util/devdata.py b/continuedev/src/continuedev/libs/util/devdata.py
new file mode 100644
index 00000000..61b4351d
--- /dev/null
+++ b/continuedev/src/continuedev/libs/util/devdata.py
@@ -0,0 +1,67 @@
+"""
+This file contains mechanisms for logging development data to files, SQL databases, and other formats.
+"""
+
+
+import json
+from datetime import datetime
+from typing import Any, Dict
+
+import aiohttp
+
+from .create_async_task import create_async_task
+from .logging import logger
+from .paths import getDevDataFilePath
+
+
+class DevDataLogger:
+ user_token: str = None
+ data_server_url: str = None
+
+ def setup(self, user_token: str = None, data_server_url: str = None):
+ self.user_token = user_token
+ self.data_server_url = data_server_url
+
+ def _to_data_server(self, table_name: str, data: Dict[str, Any]):
+ async def _async_helper(self, table_name: str, data: Dict[str, Any]):
+ if self.user_token is None or self.data_server_url is None:
+ return
+
+ async with aiohttp.ClientSession() as session:
+ await session.post(
+ f"{self.data_server_url}/event",
+ headers={"Authorization": f"Bearer {self.user_token}"},
+ json={
+ "table_name": table_name,
+ "data": data,
+ "user_token": self.user_token,
+ },
+ )
+
+ create_async_task(
+ _async_helper(self, table_name, data),
+ lambda e: logger.warning(f"Failed to send dev data: {e}"),
+ )
+
+ def _static_columns(self):
+ return {
+ "user_token": self.user_token or "NO_USER_TOKEN",
+ "timestamp": datetime.now().isoformat(),
+ }
+
+ def _to_local(self, table_name: str, data: Dict[str, Any]):
+ filepath = getDevDataFilePath(table_name)
+ with open(filepath, "a") as f:
+ json_line = json.dumps(data)
+ f.write(f"{json_line}\n")
+
+ def capture(self, table_name: str, data: Dict[str, Any]):
+ try:
+ data = {**self._static_columns(), **data}
+ self._to_data_server(table_name, data)
+ self._to_local(table_name, data)
+ except Exception as e:
+ logger.warning(f"Failed to capture dev data: {e}")
+
+
+dev_data_logger = DevDataLogger()
diff --git a/continuedev/src/continuedev/libs/util/paths.py b/continuedev/src/continuedev/libs/util/paths.py
index 9f3117d0..216386c3 100644
--- a/continuedev/src/continuedev/libs/util/paths.py
+++ b/continuedev/src/continuedev/libs/util/paths.py
@@ -31,6 +31,21 @@ def getServerFolderPath():
return path
+def getDevDataFolderPath():
+ path = os.path.join(getGlobalFolderPath(), "dev_data")
+ os.makedirs(path, exist_ok=True)
+ return path
+
+
+def getDevDataFilePath(table_name: str):
+ filepath = os.path.join(getDevDataFolderPath(), f"{table_name}.jsonl")
+ if not os.path.exists(filepath):
+ with open(filepath, "w") as f:
+ f.write("")
+
+ return filepath
+
+
def getMeilisearchExePath():
binary_name = "meilisearch.exe" if os.name == "nt" else "meilisearch"
path = os.path.join(getServerFolderPath(), binary_name)
diff --git a/continuedev/src/continuedev/plugins/steps/chat.py b/continuedev/src/continuedev/plugins/steps/chat.py
index 15740057..05931bf1 100644
--- a/continuedev/src/continuedev/plugins/steps/chat.py
+++ b/continuedev/src/continuedev/plugins/steps/chat.py
@@ -13,6 +13,7 @@ from ...core.main import ChatMessage, FunctionCall, Models, Step, step_to_json_s
from ...core.sdk import ContinueSDK
from ...libs.llm.maybe_proxy_openai import MaybeProxyOpenAI
from ...libs.llm.openai import OpenAI
+from ...libs.util.devdata import dev_data_logger
from ...libs.util.strings import remove_quotes_and_escapes
from ...libs.util.telemetry import posthog_logger
from .core.core import MessageStep
@@ -94,6 +95,13 @@ class SimpleChatStep(Step):
"provider": sdk.models.default.__class__.__name__,
},
)
+ dev_data_logger.capture(
+ "model_use",
+ {
+ "model": sdk.models.default.model,
+ "provider": sdk.models.default.__class__.__name__,
+ },
+ )
async for chunk in generator:
if sdk.current_step_was_deleted():
diff --git a/continuedev/src/continuedev/plugins/steps/core/core.py b/continuedev/src/continuedev/plugins/steps/core/core.py
index ba67c932..7766a887 100644
--- a/continuedev/src/continuedev/plugins/steps/core/core.py
+++ b/continuedev/src/continuedev/plugins/steps/core/core.py
@@ -12,6 +12,7 @@ from ....core.observation import Observation, TextObservation, UserInputObservat
from ....libs.llm import LLM
from ....libs.llm.maybe_proxy_openai import MaybeProxyOpenAI
from ....libs.util.count_tokens import DEFAULT_MAX_TOKENS
+from ....libs.util.devdata import dev_data_logger
from ....libs.util.strings import (
dedent_and_get_common_whitespace,
remove_quotes_and_escapes,
@@ -644,6 +645,10 @@ Please output the code to be inserted at the cursor in order to fulfill the user
"model_use",
{"model": model_to_use.model, "provider": model_to_use.__class__.__name__},
)
+ dev_data_logger.capture(
+ "model_use",
+ {"model": model_to_use.model, "provider": model_to_use.__class__.__name__},
+ )
try:
async for chunk in generator:
@@ -814,6 +819,8 @@ Please output the code to be inserted at the cursor in order to fulfill the user
await self.stream_rif(rif, sdk)
await sdk.ide.setSuggestionsLocked(rif.filepath, False)
+ self.name = "Generating summary"
+
class EditFileStep(Step):
filepath: str
diff --git a/continuedev/src/continuedev/server/ide.py b/continuedev/src/continuedev/server/ide.py
index 1c88d2a0..fba455c4 100644
--- a/continuedev/src/continuedev/server/ide.py
+++ b/continuedev/src/continuedev/server/ide.py
@@ -13,6 +13,7 @@ from starlette.websockets import WebSocketDisconnect, WebSocketState
from uvicorn.main import Server
from ..libs.util.create_async_task import create_async_task
+from ..libs.util.devdata import dev_data_logger
from ..libs.util.logging import logger
from ..libs.util.queue import AsyncSubscriptionQueue
from ..libs.util.telemetry import posthog_logger
@@ -346,9 +347,11 @@ class IdeProtocolServer(AbstractIdeProtocolServer):
def onAcceptRejectSuggestion(self, accepted: bool):
posthog_logger.capture_event("accept_reject_suggestion", {"accepted": accepted})
+ dev_data_logger.capture("accept_reject_suggestion", {"accepted": accepted})
def onAcceptRejectDiff(self, accepted: bool):
posthog_logger.capture_event("accept_reject_diff", {"accepted": accepted})
+ dev_data_logger.capture("accept_reject_diff", {"accepted": accepted})
def onFileSystemUpdate(self, update: FileSystemEdit):
# Access to Autopilot (so SessionManager)