テキストエディタMeryからGoogle Geminiを使うマクロ

相変わらずテキストエディタとしてMeryを使っています。コーディングはVisual Studio Codeをメインに使っていますが、MeryからChatGPTを呼び出すマクロを作って以来、開きっぱなしで使っています。軽量なテキストエディタはいいですね。さて、生成AIの世界もChatGPTのみならず、Gemini、Claudeとたくさんでてきていますが、最近のデータもアクセスできるgemini-2.0-flash-expがよろしそうなので、そちらのマクロも作ってみました。使う方法はChatGPT用マクロと同じで、質問したい内容を選択状態にしておきマクロを実行します。しばらくすると答えが帰ってきます。これもPythonマクロで記述しているのであらかじめ使える環境にする必要があります。また、Geminiを使うための API Keyの取得が必要です。API Keyの値をGOOGLE_API_KEYという名前の環境変数として設定してください。

from google import genai
from google.genai import types

#model = 'gemini-1.5-flash'
#model = 'gemini-1.5-pro'
model = 'gemini-2.0-flash-exp'
#model = 'gemini-2.0-flash-thinking-exp-1219'

# Make messages
def make_messages(text):

    roles = {'assistant': 'model', 'user': 'user'}
    messages = []
    role = 'user' # Initial role
    content = ''

    lines = text.splitlines()
    for line in lines:
        role_headline = False
        for key in roles.keys():
            if line == f'### {key}':
                if content:
                    messages.append({"role": roles[role], "parts": [{"text": content}]})
                role = key
                content = ''
                role_headline = True
                break
        if not role_headline:
            content += line + '\n'
            
    if content:
        messages.append({"role": roles[role], "parts": [{"text": content}]})

    return messages

if not window.document.selection.Text:
    window.document.selection.SelectAll()

text = window.document.selection.Text

messages = make_messages(text)

if messages and (messages[-1]["role"] == 'user'):
    window.document.selection.Text = text
    client = genai.Client()
    window.document.selection.Text = '\n\n### assistant\n\n'
    google_search_tool = types.Tool(google_search = types.GoogleSearch())
    chat = client.chats.create(
        model=model,
        config=types.GenerateContentConfig(
            tools=[google_search_tool],
            response_modalities=['TEXT'],
        ),
        history=messages[:-1],
    )
    response = chat.send_message(messages[-1]["parts"][0]["text"])
    content = response.text
    window.document.selection.Text = content
#    responses = chat.send_message(messages[-1]["content"], stream=True)
#    for chunk in responses:
#        content = chunk.text
#        window.document.selection.Text = content
    window.document.selection.Text = '\n\n### user\n\n'

データサイエンスプロジェクトの進行に合わせたファイル名のアルファベット命名法

ファイルをアルファベット順にならべると、データサイエンスプロジェクトの進行順に並ぶようにするための英文字をChat-GPTに考えてもらいもらいました。

user

データサイエンスプロジェクトに含まれるファイル名の先頭につける英字を考えています。機械学習の進行度(探索、データセットの整備、特徴量の選択、モデル構築、プロット、論文化)などがアルファベットの並び(a,b,c,...)と整合させるにはどうすればいいですか。例として考えたものを示します。

  • a: ahead advance
  • b: basic
  • c: clean
  • d: dataset
  • e: explore
  • f: feature
  • m: model
  • p: print plot publish
  • t: test
  • x: explore

assistant

データサイエンスプロジェクトの進行度に応じたファイル名の先頭に付ける英字を考えるのは、プロジェクトの整理や進行管理に役立ちます。以下は、アルファベットの並びと進行度を整合させるための提案です。

  • a: analysis (初期分析)
  • b: basic (基本的な設定や関数)
  • c: clean (データクリーニング)
  • d: dataset (データセットの準備)
  • e: explore (データ探索)
  • f: feature (特徴量の選択とエンジニアリング)
  • g: generate (データ生成や追加)
  • h: hypothesize (仮説の設定)
  • i: implement (実装)
  • j: join (データの統合)
  • k: knowledge (知識の蓄積、参考文献)
  • l: learn (学習、モデルの訓練)
  • m: model (モデル構築)
  • n: normalize (データの正規化)
  • o: optimize (最適化)
  • p: plot / publish (プロット/論文化)
  • q: quality (品質評価、検証)
  • r: review (レビューと改良)
  • s: summarize (結果の要約)
  • t: test (テスト)
  • u: update (モデルやデータの更新)
  • v: visualize (可視化)
  • w: workflow (作業手順)
  • x: experiment (実験)
  • y: yield (出力、成果物)
  • z: z-finalize (最終確認および完了)

このリストは、プロジェクトのニーズや進行状況に応じて調整が可能です。特に、pに関しては「plot(可視化)」と「publish(論文化)」を同時に含めても良いかもしれません。これにより、ファイル名の一貫性と分かりやすさを保ちながらプロジェクトを管理することができます。

Visual Studio CodeからOpen AI ChatGPTを使う拡張機能

以前、テキストエディタMeryからOpenAI APIをたたくマクロをつくりました。

テキストエディタMeryからOpenAI ChatGPTを使うマクロ - attosci diary

しかし、最近はVSCodeを使うことも多くなりましたので、拡張機能として同様な動作をさせたいと思い、TypeScritバージョンを作りました。環境変数としてOPENAI_API_KEYにOpenAI API Keyを設定する必要があります。

拡張機能の作り方は以下のサイトを参考にしました。使うモデルはコードの中に埋め込んでいるので必要に応じて修正する必要があります。

VSCode Extensions(拡張機能) 自作入門 〜VSCodeにおみくじ機能を追加する〜 #VSCode - Qiita

import * as vscode from 'vscode';
import { OpenAI } from 'openai';

export function activate(context: vscode.ExtensionContext) {

    console.log('Extension "vscode-openai" is now active!');

    const disposable = vscode.commands.registerCommand('vscode-openai.sendToChatGPT', async () => {
        const editor = vscode.window.activeTextEditor;

        if (!editor) {
            vscode.window.showInformationMessage('No active editor!');
            return; // No active editor
        }

        const document = editor.document;
        const selection = editor.selection;
        const text = selection.isEmpty ? document.getText() : document.getText(selection);

        try {
            const response = await sendToChatGPT(text);
            appendTextToDocument(editor, response);
        } catch (error) {
            if (error instanceof Error) {
                // We can now safely read the message property of the Error instance
                vscode.window.showErrorMessage('Error communicating with OpenAI: ' + error.message);
            } else {
                // For other types of thrown values that are not Error instances
                vscode.window.showErrorMessage('An unknown error occurred.');
            }
        }
    });

    context.subscriptions.push(disposable);
}

async function sendToChatGPT(text: string): Promise<string> {
    // Obtain OpenAI API Key from environment variable OPENAI_API_KEY
    const apiKey = process.env.OPENAI_API_KEY;
    if (!apiKey) {
        throw new Error('OpenAI API key is not set in the environment variable OPENAI_API_KEY.');
    }
    const openai = new OpenAI({ apiKey: apiKey });
    const model = vscode.workspace.getConfiguration().get('vscode-openai.model', 'gpt-4-turbo');
    const messages: any[] = makeMessages(text);
    const payload = {
        model: model,
        messages: messages
    };

    const response = await openai.chat.completions.create(payload);
    if (response.choices && response.choices.length > 0 && response.choices[0].message.content) {
        return response.choices[0].message.content;
    } else {
        throw new Error('Invalid response from the API.');
    }
}

function appendTextToDocument(editor: vscode.TextEditor, text: string) {
    editor.edit(editBuilder => {
        const document = editor.document;
        let selection = editor.selection;
        let position: vscode.Position;

        if (!selection.isEmpty) {
            position = selection.end;
        } else {
            const lastLine = document.lineAt(document.lineCount - 1);
            position = new vscode.Position(document.lineCount - 1, lastLine.text.length);
        }

        const assistantMarker = "\n\n### assistant\n\n";
        const userMarker = "\n\n### user\n\n";
        const fullText = assistantMarker + text + userMarker;
        editBuilder.insert(position, fullText);
    });
}

function makeMessages(text: string): any {
    const lines = text.split('\n');

    let messages: any[] = [];
    const roles: string[] = ['system', 'assistant', 'user'];
    let current_role: string = 'user';
    let content: string = '';

    for (const line of lines) {
        let roleHeadline: boolean = false;
        for (const role in roles) {
            if (line.startsWith(`### ${role}`)) {
                if (content.length > 0) {
                    messages.push({ "role": current_role, "content": content});
                }
                current_role = role;
                content = '';
                roleHeadline = true;
                break;
            }
        }
        if (!roleHeadline) {
            content += line + '\n';
        }
    }
    if (content.length > 0) {
        messages.push({ "role": current_role, "content": content});
    }
    return messages;
}

export function deactivate() {}

テキストエディタMeryからOpenAI ChatGPTを使うマクロ

ここ数年はテキストエディタとしてMeryを使っています。最近はVisual Studio Codeをコーディングに使う事がめっきり多くなりましたが、軽量なテキストエディタはなかなか手放せません。さて、最近流行りのChatGPTですが、これをエディタの中で使うと便利ではないかと思い立ちMeryのマクロを作ってみました。質問したい内容を選択状態にしておきマクロを実行します。しばらくすると答えが帰ってきます。Pythonマクロなので若干の設定が必要なことと、OpenAI API Keyの取得が必要です。OpenAI API Keyの値をOPENAI_API_KEYという名前の環境変数として設定してください。

from openai import OpenAI
# import json


def make_messages(text):

    roles = ['system', 'assistant', 'user']
    messages = []
    role = 'user'
    content = ''

    lines = text.splitlines()
    for line in lines:
        role_headline = False
        for key in roles:
            if line == f'### {key}':
                if content:
                    messages.append({"role": role, "content": content})
                role = key
                content = ''
                role_headline = True
                break
        if not role_headline:
            content += line + '\n'    

    if content:
        messages.append({"role": role, "content": content})
    
    return messages


client = OpenAI()

if not window.document.selection.Text:
    window.document.selection.SelectAll()

text = window.document.selection.Text

messages = make_messages(text)

# window.alert(json.dumps(messages))

if messages:
    response = client.chat.completions.create(model="gpt-4-turbo", messages=messages)
    content = response.choices[0].message.content.lstrip()    
    window.document.selection.Text = text + '\n\n### assistant\n\n' + content + '\n\n### user\n\n'
  • MeryマクロをPythonで記述できる様にする方法は次のリンクを参考にしました。 知っておくと便利なテキストエディター「Mery」の 10 個の小技

  • このマクロを実行するためには OpenAI API のキーを入手する必要があります。

  • 見出し### systemをつけると以下をsystem contentとして扱います。

  • 見出し### userをつけると以下をuser contentとして扱います。

  • 見出し### assistantをつけると以下をassistant contentとして扱います。

ユピテルY-300dPの画像から加速度データを得る

最近、ドライブレコーダーを買いました。 ユピテル YupiteruY-300dPです。 発売は2021年と少々古いのですが、性能が良く値段が手頃なので気に入っています。 このドラレコは衝突や急ブレーキ、急ハンドルなどで異常な加速度を検知した場合、 前後40秒のデータを別フォルダに格納する機能がついています。 その際に加速度の閾値を適切に決めないと頻繁にアラーム音とともに動作してしまうので 設定を調整する必要があります。 画像をPCで確認するプログラムとのことで、 調べてみると字幕データとしてフレームごとに記録されているようです。 というわけでPythonコードを書いてみました。

# AVI Audio Video Interface
# RIFF Resource Interchange File Format

import struct

#
# Read RIFF Header
#
def read_RIFF_header(f):
    buffer = f.read(12)
    riff, size, type = struct.unpack('<4sI4s', buffer)
    if riff != b'RIFF' or type != b'AVI ':
        raise ValueError('Not a valid AVI file')
    return size

def read_list(f, read_data=True):
    buffer = f.read(12)
    id, size, type = struct.unpack('<4sI4s', buffer)
    if id != b'LIST':
        raise ValueError('Not LIST.')
    if read_data:
        buffer = f.read(size - 4)
        if size % 2 == 1:
            f.read(1) # Read one more byte if size is odd.
        return size, type, buffer
    return size, type

def read_chunk(f, read_data=True):
    buffer = f.read(8)
    id, size = struct.unpack('<4sI', buffer)
    if read_data:
        buffer = f.read(size)
        if size % 2 == 1:
            f.read(1) # Read one more byte if the size is odd.
        return id, size, buffer
    return id, size

file = '~~~.avi'
f = open(file, 'rb')
#
# Check RIFF Header
#
size = read_RIFF_header(f)
size, type = read_list(f, read_data=False)
if type != b'hdrl':
    raise ValueError('list_type is not hdrl.')
#
# Main AVI Header
#
id, size, buffer = read_chunk(f)
if id != b'avih':
    raise ValueError('chunk_type is not avih.')
#
# Stream Header Lists
# fccType = 'vids', 'auds', 'txts'
#
for i in range(3):
    size, type, buffer = read_list(f)
    if type != b'strl':
        raise ValueError('list_type is not strl.')
#
# JUNK Chunks
#
for i in range(2):
    id, size, buffer = read_chunk(f)
    if id != b'JUNK':
        raise ValueError('chunk_type is not JUNK.')
#
# movi List
#
movi_size, type = read_list(f, read_data=False)
if type != b'movi':
    raise ValueError('listtype is not movi.')


current_size = 0
ax_list = []
ay_list = []
az_list = []
while current_size < movi_size:
    id, size, buffer = read_chunk(f)
    if id == b'02tx':
        f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, \
            ax, ay, az, n, speed, latitude, longitude, unix_time = struct.unpack('<13fI3fI', buffer)
        ax_list.append(ax)
        ay_list.append(ay)
        az_list.append(az)
        print(ax, ay, az, n, speed, latitude, longitude, unix_time)
    current_size += size

f.close()

私が購入したドラレコの型番はY-300dpなのですが、 LaBoon!!さんのサイトでは、 同様なハードが様々な型番(WDT700c, SN-TW9900d, SN-TW9800d, Y-300C, Y-300R, Y-210R, SN-TW99c, SN-TW84d)で販売されているとの事で、 ひょっとしたらこれらのドラレコでもデータが抽出できるかもしれません。