Skip to content

Commit

Permalink
✨ Allow logging to disk (#237)
Browse files Browse the repository at this point in the history
* WIP logging updates

Signed-off-by: John Matthews <jwmatthews@gmail.com>

* Logging to disk via podman compose working

Signed-off-by: John Matthews <jwmatthews@gmail.com>

* Tweak to log levels

Signed-off-by: John Matthews <jwmatthews@gmail.com>

* Cleanup tweaks

Signed-off-by: John Matthews <jwmatthews@gmail.com>

* Ensure 'load-data' from podman compose up can log to disk

Signed-off-by: John Matthews <jwmatthews@gmail.com>

---------

Signed-off-by: John Matthews <jwmatthews@gmail.com>
  • Loading branch information
jwmatthews committed Jul 22, 2024
1 parent 74efb3d commit ece6b12
Show file tree
Hide file tree
Showing 17 changed files with 138 additions and 159 deletions.
7 changes: 4 additions & 3 deletions compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ x-common-variables: &kai-variables
DEMO_MODE: "False"
HUB_URL: ${HUB_URL:-https://tackle-konveyor-tackle.apps.example.com/hub}
IMPORTER_ARGS:
LOGLEVEL: info
LOG_LEVEL: info
LOG_DIR: "/podman_compose/logs"
NUM_WORKERS: 8
USE_HUB_IMPORTER: ${USE_HUB_IMPORTER:-False}

Expand All @@ -27,7 +28,7 @@ services:
OPENAI_API_KEY:
image: ${IMAGE:-quay.io/konveyor/kai}:${TAG:-stable}
volumes:
- ${PWD}:/podman_compose:ro,z
- ${PWD}:/podman_compose:rw,z
ports:
- "8080:8080"
depends_on:
Expand All @@ -39,7 +40,7 @@ services:
MODE: importer
image: ${IMAGE:-quay.io/konveyor/kai}:${TAG:-stable}
volumes:
- ${PWD}:/podman_compose:ro,z
- ${PWD}:/podman_compose:rw,z
depends_on:
- kai_db
profiles:
Expand Down
13 changes: 10 additions & 3 deletions example/run_demo.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python

import json
import logging
import os
import sys
import time
Expand All @@ -12,8 +13,10 @@

# Ensure that we have 'kai' in our import path
sys.path.append("../../kai")
from kai import Report
from kai.kai_logging import KAI_LOG
from kai.kai_logging import formatter
from kai.report import Report

KAI_LOG = logging.getLogger(__name__)

SERVER_URL = "http://0.0.0.0:8080"
APP_NAME = "coolstore"
Expand Down Expand Up @@ -249,7 +252,11 @@ def run_demo(report):


if __name__ == "__main__":
KAI_LOG.setLevel("info".upper())
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)
KAI_LOG.addHandler(console_handler)
KAI_LOG.setLevel("DEBUG")

start = time.time()
coolstore_analysis_dir = "./analysis/coolstore/output.yaml"
r = Report.load_report_from_file(coolstore_analysis_dir)
Expand Down
119 changes: 0 additions & 119 deletions kai/__init__.py
Original file line number Diff line number Diff line change
@@ -1,120 +1 @@
__version__ = "0.0.1"

from typing import Annotated, List

import typer

from kai.report import Report
from kai.result import LLMResult
from kai.service.incident_store.incident_store import IncidentStore

# from typing_extensions import Annotated


app = typer.Typer()

# report_app = typer.Typer()
# result_app = typer.Typer()

# app.add_typer(report_app, name="report", help="Generate a markdown report from a raw analysis yaml.")
# app.add_typer(result_app, name="result", help="Generate patches for given violations and incidents from an analysis yaml")


@app.command()
def report(analysis_path: str, output_dir: str):
"""
Generate a Markdown report of a given analysis
YAML to be read by a human
"""
report = Report.load_report_from_file(analysis_path)
r = dict(report)
print(f"We have results from {len(r.keys())} RuleSet(s) in {analysis_path}\n")
report.write_markdown(output_dir)


@app.command()
def generate(
path_to_report: str,
path_to_source: str,
example_initial_branch: str,
example_solved_branch: str,
path_to_output: str,
limit_rulesets: Annotated[List[str], typer.Option("--ruleset", "-r")] = None,
limit_violations: Annotated[List[str], typer.Option("--violation", "-v")] = None,
model: Annotated[str, typer.Option("--model", "-m")] = "gpt-3.5-turbo-16k",
):
# model: Annotated[Optional[str], typer.Argument()] = "gpt-3.5-turbo-16k"):
"""
Generate patches for given violations and incidents from an analysis yaml report
- path_to_report: Path to the analysis yaml report
- path_to_source: Path to the source code to be patched
- example_initial_branch: Branch name for the initial state of the source code
- example_solved_branch: Branch name for the solved state of the source code
- path_to_output: Path to the output directory for the patches
- limit_rulesets: Limit to specific rulesets (defaults to 'None', meaning to run all)
- limit_violations: Limit to specific violations (defaults to 'None', meaning to run all)
- model: Model name to use for generating the patches (defaults to 'gpt-3.5-turbo-16k', 'gpt-4-1106-preview' is another good option)
"""
print(
f"Generating patches for {path_to_report} for example app at {path_to_source}"
)
print(f"Initial branch: {example_initial_branch}")
print(f"Solved branch: {example_solved_branch}")
print(f"Output directory: {path_to_output}")
print(f"Model: {model}")
print(f"Limit to ruleset(s): {limit_rulesets}")
print(f"Limit to violation(s): {limit_violations}")

llmResult = LLMResult(path_to_source, example_initial_branch, example_solved_branch)
llmResult.parse_report(path_to_report)
llmResult.process(path_to_output, model, limit_rulesets, limit_violations)
print(f"Completed processing, output written to {path_to_output}\n")


@app.command()
def load(report_path: str, output_dir: str):
"""
Load the incident store with the given applications
write the cached_violations to a file for later use
"""
incident_store = IncidentStore(report_path, output_dir)
incident_store.load_incident_store()


@app.command()
def patch(ruleset: str, violation: str, report_path: str, output_dir: str):
"""
Generate patches for a specific violation
"""
print(f"Generating patches for {ruleset} - {violation}")
incident_store = IncidentStore(report_path, output_dir)
patches = incident_store.get_solved_issue(ruleset, violation)
if len(patches) == 0:
print(f"No patches found for {ruleset} - {violation}")
else:
for patch in patches:
print(f"Patch: {patch}")
return patches


@app.command()
def common(ruleset: str, violation: str, report_path: str, output_dir: str):
"""
Find common violations for a specific violation
"""
print(f"Finding common violations for {ruleset} - {violation}")
incident_store = IncidentStore(report_path, output_dir)
violations = incident_store.find_common_violations(ruleset, violation)
if violations is None:
print(f"No common violations found for {ruleset} - {violation}")
for violation in violations:
print(f"Violation: {violation}")
return violations
2 changes: 2 additions & 0 deletions kai/config.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
log_level = "info"
file_log_level = "debug"
log_dir = "$pwd/logs"
demo_mode = false
trace_enabled = true

Expand Down
3 changes: 2 additions & 1 deletion kai/embedding_provider.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logging
import random
from abc import ABC, abstractmethod
from enum import Enum
Expand All @@ -9,7 +10,7 @@
from psycopg2 import sql
from psycopg2.extensions import connection

from kai.kai_logging import KAI_LOG
KAI_LOG = logging.getLogger(__name__)


class TrimStrategy(Enum):
Expand Down
4 changes: 3 additions & 1 deletion kai/hub_importer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python

import argparse
import logging
import os
import pprint
import tempfile
Expand All @@ -13,11 +14,12 @@
from git import GitCommandError, Repo
from pydantic import BaseModel, Field

from kai.kai_logging import KAI_LOG
from kai.models.kai_config import KaiConfig
from kai.report import Report
from kai.service.incident_store import Application, IncidentStore

KAI_LOG = logging.getLogger(__name__)


# BaseModel that also acts as a dict
class KaiBaseModel(BaseModel):
Expand Down
63 changes: 58 additions & 5 deletions kai/kai_logging.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,63 @@
import logging
import os

KAI_LOG = logging.getLogger(__name__)
console_handler = logging.StreamHandler()
from kai.models.kai_config import KaiConfig

parent_log = logging.getLogger("kai")

# console_handler = logging.StreamHandler()
# formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
formatter = logging.Formatter(
"%(levelname)s - %(asctime)s - [%(filename)20s:%(lineno)-4s - %(funcName)20s()] - %(message)s"
"%(levelname)s - %(asctime)s - %(name)s - [%(filename)20s:%(lineno)-4s - %(funcName)20s()] - %(message)s"
)
console_handler.setFormatter(formatter)
KAI_LOG.addHandler(console_handler)


def process_log_dir_replacements(log_dir: str):
##
# We want to replace $pwd with the location of the Kai project directory,
# this is needed to help with specifying from configuration
##
if log_dir.startswith("$pwd"):
log_dir = log_dir.replace(
"$pwd", os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
)
return log_dir


def setup_console_handler(logger, log_level: str = "INFO"):
console_handler = logging.StreamHandler()
console_handler.setLevel(log_level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
print(f"Console logging for '{parent_log.name}' is set to level '{log_level}'")


def setup_file_handler(
logger, log_file_name: str, log_dir: str, log_level: str = "DEBUG"
):
# Ensure any needed log directories exist
log_dir = process_log_dir_replacements(log_dir)
log_file_path = os.path.join(log_dir, log_file_name)
if log_dir.startswith("$pwd"):
log_dir = os.path.join(os.getcwd(), log_dir[5:])
os.makedirs(os.path.dirname(log_file_path), exist_ok=True)

file_handler = logging.FileHandler(log_file_path)
file_handler.setLevel(log_level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)
print(
f"File logging for '{logger.name}' is set to level '{log_level}' writing to file: '{log_file_path}'"
)


def initLogging(console_log_level, file_log_level, log_dir, log_file="kai_server.log"):
setup_console_handler(parent_log, console_log_level)
setup_file_handler(parent_log, log_file, log_dir, file_log_level)
# Attempt to set the parent log level to
# most persmissive and allow child loggers to control what is filtered or not
parent_log.setLevel("DEBUG")


def initLoggingFromConfig(config: KaiConfig):
initLogging(config.log_level.upper(), config.file_log_level.upper(), config.log_dir)
4 changes: 3 additions & 1 deletion kai/llm_io_handler.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import itertools
import logging
import os
import time
import traceback
Expand All @@ -16,7 +17,6 @@
)

from kai.constants import PATH_TEMPLATES
from kai.kai_logging import KAI_LOG
from kai.model_provider import ModelProvider
from kai.models.file_solution import guess_language, parse_file_solution_content
from kai.service.incident_store.incident_store import IncidentStore
Expand All @@ -25,6 +25,8 @@
LLM_RETRIES = 5
LLM_RETRY_DELAY = 10

KAI_LOG = logging.getLogger(__name__)


def get_prompt(
model_provider: ModelProvider,
Expand Down
3 changes: 2 additions & 1 deletion kai/models/file_solution.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import logging
import re

from pydantic import BaseModel
from pygments import lexers
from pygments.util import ClassNotFound

from kai.kai_logging import KAI_LOG
KAI_LOG = logging.getLogger(__name__)


class FileSolutionContent(BaseModel):
Expand Down
4 changes: 4 additions & 0 deletions kai/models/kai_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ class KaiConfigModels(BaseModel):

class KaiConfig(BaseModel):
log_level: str = "info"
file_log_level: str = "info"
log_dir: str = os.path.join(
os.path.dirname(os.path.realpath(__file__)), "../../logs"
)
demo_mode: bool = False
trace_enabled: bool = False

Expand Down
3 changes: 2 additions & 1 deletion kai/report.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import hashlib
import json
import logging
import os
import shutil
from io import StringIO

import yaml

from kai.kai_logging import KAI_LOG
KAI_LOG = logging.getLogger(__name__)


class Report:
Expand Down
4 changes: 3 additions & 1 deletion kai/result.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
__all__ = ["LLMResult"]

import logging
import os

from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain_community.chat_models import ChatOpenAI

from kai.constants import PATH_TEMPLATES
from kai.kai_logging import KAI_LOG

from .report import Report
from .scm import GitDiff

KAI_LOG = logging.getLogger(__name__)


class LLMResult:
"""The intent of this class is to help us form several Prompt examples using a single application
Expand Down
3 changes: 2 additions & 1 deletion kai/scm.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
__all__ = ["GitDiff"]

import logging

from git import BadName, Repo

from kai.kai_logging import KAI_LOG
KAI_LOG = logging.getLogger(__name__)


class GitDiff:
Expand Down
Loading

0 comments on commit ece6b12

Please sign in to comment.