import core.bitop;
import std.algorithm;
import std.bigint;
import std.bitmanip;
import std.conv;
import std.exception;
import std.functional;
import std.math;
import std.numeric;
import std.random;
import std.range;
import std.stdio;
import std.string;
import std.traits;
import std.typecons;

pure nothrow @nogc
T gcdext(T) (T a, T b, ref T x, ref T y) {
  if (!b) {
    x = 1;
    y = 0;
    return a;
  }
  T res = gcdext (b, a % b, y, x);
  y -= x * (a / b);
  return res;
}

pure
X genericPower(alias mul, X, Y) (X x, Y y, X one = 1.to!X)
  if (isUnsigned!Y) {
  X a = one, b = x;
  while (y > 0) {
    if (y & 1) {
      a = binaryFun!mul (a, b);
    }
    b = binaryFun!mul (b, b);
    y >>>= 1;
  }
  return a;
}

struct IntM(int q = 1_000_000_007) {
  alias N = IntM!q;
  private:
  int v;
  invariant () { assert (v >= 0 && v < q); }
  pure nothrow @nogc
  static int from(T) (const T m) if (isIntegral!T) {
    int v = m % q;
    static if (isSigned!T) {
      if (v < 0) {
        v += q;
      }
    }
    return v;
  }
  public:
  pure nothrow @nogc
  this(T) (const T m) if (isIntegral!T) {
    v = from!T (m);
  }
  pure nothrow @nogc
  N opAssign(T) (const T m) if (isIntegral!T) {
    v = from!T (m);
    return this;
  }
  pure nothrow @nogc
  N opUnary (string op : "-")() const {
    return N (-v);
  }
  pure nothrow @nogc
  ref N opUnary (string op : "++")() {
    if (++v >= q) {
      v -= q;
    }
    return this;
  }
  pure nothrow @nogc
  ref N opUnary (string op : "--")() {
    if (--v < 0) {
      v += q;
    }
    return this;
  }
  pure nothrow @nogc
  ref N opOpAssign (string op : "+")(in N rhs) {
    v = (v + rhs.v) % q;
    return this;
  }
  pure nothrow @nogc
  ref N opOpAssign (string op : "-")(in N rhs) {
    v = (v - rhs.v + q) % q;
    return this;
  }
  pure nothrow @nogc
  ref N opOpAssign (string op : "*")(in N rhs) {
    v = (v.to!(long) * rhs.v) % q;
    return this;
  }
  pure nothrow @nogc
  ref N opOpAssign (string op : "/")(in N rhs) {
    return this *= rhs.inversion ();
  }
  pure nothrow @nogc
  ref N opOpAssign (string op)(in int rhs) if (op == "+" || op == "-" || op == "*" || op == "/") {
    mixin ("return this " ~ op ~ "= N(rhs);");
  }
  pure nothrow @nogc
  N opBinary (string op)(in N rhs) const if (op == "+" || op == "-" || op == "*" || op == "/") {
    N t = this;
    mixin ("t " ~ op ~ "= rhs;");
    return t;
  }
  pure nothrow @nogc
  N opBinary(string op,T)(const T rhs) const if (isIntegral!T && (op == "+" || op == "-" || op == "*" || op == "/")) {
    mixin ("return this " ~ op ~ " N(rhs);");
  }
  pure nothrow @nogc
  N opBinaryRight(string op,T)(const T rhs) const if (isIntegral!T && (op == "+" || op == "*")) {
    mixin ("return this " ~ op ~ " N(rhs);");
  }
  pure nothrow @nogc
  N inversion () const {
    int x, y;
    immutable g = gcdext!int (v, q, x, y);
    assert (g == 1);
    return N (x);
  }
  pure nothrow @nogc
  N opBinary (string op : "^^")(in ulong rhs) const {
    return genericPower! ("a * b", N, ulong) (this, rhs);
  }
  pure nothrow @nogc
  int opCast(T : int)() const { return v; }
  pure nothrow @nogc
  int opCmp (const N rhs) const {
    if (v < rhs.v) {
      return -1;
    }
    if (v > rhs.v) {
      return 1;
    }
    return 0;
  }
  pure nothrow
  string toString() const { return v.text; }
}

final class LinearSieve(T)
  if (isIntegral!T) {
  private:
  BitArray composite;
  T[] f;
  int[] primes;
  public:
  T opIndex (size_t index) const {
    return f[index];
  }
  auto opSlice(size_t begin, size_t end) const {
    return f[begin .. end];
  }
  this (const int n, T function (const int) prime, T function (const T, const int) divides) {
    composite.length = n;
    f = new T[n];
    f[1] = 1;
    foreach (i; 2 .. n) {
      if (!composite[i]) {
        primes ~= i;
        f[i] = prime (i);
      }
      foreach (j; primes) {
        immutable k = i * j;
        if (k >= n) {
          break;
        }
        composite[k] = true;
        if (!(i % j)) {
          f[k] = divides(f[i], j);
          break;
        } else {
          f[k] = f[i] * f[j];
        }
      }
    }
  }
}

auto mobiusSieve (const int n) {
  return new LinearSieve!int (n, function int (const int p) { return -1; }, function int (const int acc, const int j) {
    return 0;
  });
}

alias N = IntM!1_000_000_007;

void main() {
  int n, k;
  readf (" %d %d", &n, &k);
  auto mu = mobiusSieve (k + 1);
  auto c = new N[k+1];
  auto b = new bool[k+1];
  N f (int m) {
    if (b[m]) return c[m];
    N ans = 0;
    foreach (i; 1 .. m + 1) {
      int s = mu[i];
      if (s != 0) {
        auto u = N (m / i);
        if (s > 0) {
          ans += u ^^ n; 
        } else {
          ans -= u ^^ n; 
        }
      }
    }
    b[m] = true;
    c[m] = ans;
    debug stderr.writefln ("f(%d) = %s", m, ans);
    return ans;
  }
  N res = 0;
  foreach (d; 1 .. k + 1) {
    res += d * f (k / d);
  }
  writeln (res);
}



Measured memory_bytes: 4000